#1模塊定義 # 模塊(Module)就是包含代碼的文件,不必定是Python代碼,有四種代碼類型的模塊: # •使用Python寫的程序( .py文件) # •C或C++擴展(已編譯爲共享庫或DLL文件) # •包(包含多個模塊) # •內建模塊(使用C編寫並已連接到Python解釋器內)
#2爲何用模塊 # 使用模塊能夠提升代碼的可維護性和重複使用,還能夠避免函數名和變量名衝突。相同名字的函數和變量徹底能夠分別存在不一樣的模塊中, # 因此編寫本身的模塊時,沒必要考慮名字會與其餘模塊衝突,但要注意儘可能不要與內置函數名字衝突。
#(1)自定義模塊
#my_module.py
print('from the my_module.py') money = 1000
def read1(): print('my_module -> read1 -> money',money) def read2(): print('my_module -> read2 calling read1') read1() def change(): global money money = 0 #(2)重複導入模塊 # 模塊能夠包含可執行的語句和函數的定義,這些語句的目的是初始化模塊,它們只在模塊名第一次遇到導入import語句時才執行(import語句是 # 能夠在程序中的任意位置使用的,且針對同一個模塊很import屢次,爲了防止你重複導入,python的優化手段是:第一次導入後就將模塊名加載到 # 內存了,後續的import語句僅是對已經加載大內存中的模塊對象增長了一次引用,不會從新執行模塊內的語句),以下
import my_module #只在第一次導入時才執行my_module.py內代碼,此處的顯式效果是隻打印一次'from the my_module.py'
'''結果: from the my_module.py '''
import my_module import my_module import my_module import sys print(sys.modules) #sys.modules 顯示已加載的模塊信息,sys.modules返回一個字典。
#(3)模塊名稱空間 # 每一個模塊都是一個獨立的名稱空間,定義在這個模塊中的函數,把這個模塊的名稱空間當作全局名稱空間,這樣咱們在編寫本身的模塊時, # 就不用擔憂咱們定義在本身模塊中全局變量會在被導入時,與使用者的全局變量衝突
#測試1 money和my_module.money不衝突
import my_module mmoney = 10
print(my_module.money) '''結果: from the my_module.py 1000 '''
#測試2 read1 與 my_module.read1不衝突
import my_module def read1(): print('***************') my_module.read1() '''結果: my_module -> read1 -> money 1000 '''
#測試3 執行my_module.change()操做的全局變量money仍然是my_module中的
import my_module money = 1 my_module.change() print(money) '''結果: 1 '''
(4)模塊導入過程html
總結:首次導入模塊my_module時會作三件事:python
1.爲源文件(my_module模塊)建立新的名稱空間,在my_module中定義的函數和方法如果使用到了global時訪問的就是這個名稱空間。mysql
2.在新建立的命名空間中執行模塊中包含的代碼,見初始導入import my_modulesql
1 提示:導入模塊時到底執行了什麼?
2
3 In fact function definitions are also ‘statements’ that are ‘executed’; the execution of a module-level function definition enters the function name in the module’s global symbol table.
4 事實上函數定義也是「被執行」的語句,模塊級別函數定義的執行將函數名放入模塊全局名稱空間表,用globals()能夠查看
3.建立名字my_module來引用該命名空間shell
1 這個名字和變量名沒什麼區別,都是‘第一類的’,且使用my_module.名字的方式能夠訪問my_module.py文件中定義的名字,my_module.名
字與test.py中的名字來自兩個徹底不一樣的地方。
(5)模塊重命名windows
爲模塊名起別名,至關於m1=1;m2=m1 緩存
1 import my_module as sm
2 print(sm.money)
示例1:架構
兩種sql模塊mysql和oracle,根據用戶的輸入,選擇不一樣的sql功能oracle
#mysql.py
def sqlparse(): print('from mysql sqlparse')
#oracle.py
def sqlparse(): print('from oracle sqlparse')
#test.py
db_type=input('>>: ') #輸入mysql 或oracle
if db_type == 'mysql': import mysql as db elif db_type == 'oracle': import oracle as db db.sqlparse() #根據輸入調用不一樣模塊中的sqlparse函數
示例2:app
爲已經導入的模塊起別名的方式對編寫可擴展的代碼頗有用,假設有兩個模塊xmlreader.py和csvreader.py,它們都定義了函數read_data(filename):用來從文件中讀取一些數據,但採用不一樣的輸入格式。能夠編寫代碼來選擇性地挑選讀取模塊,例如
if file_format == 'xml': import xmlreader as reader elif file_format == 'csv': import csvreader as reader data=reader.read_date(filename)
(6)一行導入多個模塊
1 import sys,os,re
格式:
from modname import name1[, name2[, ... nameN]]
而from 語句至關於import,也會建立新的名稱空間,可是將my_module中的名字直接導入到當前的名稱空間中,在當前名稱空間中,直接使用名字就能夠了
# 測試1:導入的函數read1,執行時仍然回到my_module.py中尋找全局變量money
from my_module import read1 money = 2000 read1() #調用read1時,所用money變量仍是從模塊的命名空間中查找,和當前空間的money變量沒有關係
'''結果: from the my_module.py my_module -> read1 -> money 1000 '''
# 測試2:導入的函數read2,執行時須要調用read1(),仍然回到my_module.py中找read1()
from my_module import read2 def read1(): print('**********/*') read2() '''結果: from the my_module.py my_module -> read2 calling read1 my_module -> read1 -> money 1000 '''
# 測試3:導入的函數read1,被當前位置定義的read1覆蓋掉了 #若是當前有重名read1或者read2,那麼會有覆蓋效果。
from my_module import read1 def read1(): print('*//*/*/*/*/*/*/') read1() '''結果: from the my_module.py *//*/*/*/*/*/*/ '''
# 測試4:python中的變量賦值不是一種存儲操做,而只是一種綁定關係
from my_module import money,read1 money = 2000
print(money) '''結果: from the my_module.py 2000 ''' read1() '''結果: my_module -> read1 -> money 1000 '''
from my_module import read1 as read
from my_module import (read1, read2, money)
from my_module import * 把my_module中全部的不是如下劃線(_)開頭的名字都導入到當前位置,大部分狀況下咱們的python程序不該該使用這種導入方式,由於*你不知道你導入什麼名字,頗有可能會覆蓋掉你以前已經定義的名字。
from my_module import * #將模塊my_module中全部的名字都導入到當前名稱空間
print(money)
print(read1)
print(read2)
print(change)
''' 執行結果: from the my_module.py 1000 <function read1 at 0x1012e8158> <function read2 at 0x1012e81e0> <function change at 0x1012e8268> '''
在my_module.py中新增一行
__all__=['money','read1'] #這樣在另一個文件中用from my_module import *就這能導入列表中規定的兩個名字
*若是my_module.py中的名字前加_,即_money,則from my_module import *,則_money不能被導入
咱們能夠經過模塊的全局變量__name__來查看模塊名:
當作腳本運行:
__name__ 等於'__main__'
當作模塊導入:
__name__= 模塊名
做用:用來控制.py文件在不一樣的應用場景下執行不一樣的邏輯
if __name__ == '__main__':
def fib(n): a, b = 0, 1
while b < n: #若是n 大於 b 開始循環
print(b, end=' ') #打印b ,並將print結束設置爲空
a, b = b, a+b # a = b , b = a + b
print() if __name__ == "__main__": #判斷模塊名是不是__main__
print(__name__) #打印輸出 __name__ 對應的模塊名
num = input('num :') #等待用戶輸入num數字值
fib(int(num)) #調用fib函數時,將整數num當作實參傳遞進去
python解釋器在啓動時會自動加載一些模塊,可使用sys.modules查看
在第一次導入某個模塊時(好比my_module),會先檢查該模塊是否已經被加載到內存中(當前執行文件的名稱空間對應的內存),若是有則直接引用
若是沒有,解釋器則會查找同名的內建模塊,若是尚未找到就從sys.path給出的目錄列表中依次尋找my_module.py文件。
因此總結模塊的查找順序是:內存中已經加載的模塊->內置模塊->sys.path路徑中包含的模塊
sys.path的初始化的值來自於:
The directory containing the input script (or the current directory when no file is specified).
PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).
The installation-dependent default.
須要特別注意的是:咱們自定義的模塊名不該該與系統內置模塊重名。雖然每次都說,可是仍然會有人不停的犯錯。
在初始化後,python程序能夠修改sys.path,路徑放到前面的優先於標準庫被加載。
1 >>> import sys 2 >>> sys.path.append('/a/b/c/d') 3 >>> sys.path.insert(0,'/x/y/z') #排在前的目錄,優先被搜索
注意:搜索時按照sys.path中從左到右的順序查找,位於前的優先被查找,sys.path中還可能包含.zip歸檔文件和.egg文件,python會把.zip歸檔文件當成一個目錄去處理。
#首先製做歸檔文件:zip module.zip foo.py bar.py
import sys sys.path.append('module.zip') import foo,bar #也可使用zip中目錄結構的具體位置
sys.path.append('module.zip/lib/python') #windows下的路徑不加r開頭,會語法錯誤
sys.path.insert(0,r'C:\Users\Administrator\PycharmProjects\a')
至於.egg文件是由setuptools建立的包,這是按照第三方python庫和擴展時使用的一種常見格式,.egg文件實際上只是添加了額外元數據(如版本號,依賴項等)的.zip文件。
須要強調的一點是:只能從.zip文件中導入.py,.pyc等文件。使用C編寫的共享庫和擴展塊沒法直接從.zip文件中加載(此時setuptools等打包系統有時能提供一種規避方法),且從.zip中加載文件不會建立.pyc或者.pyo文件,所以必定要事先建立他們,來避免加載模塊是性能降低。
官網解釋
#官網連接:https://docs.python.org/3/tutorial/modules.html#the-module-search-path
搜索路徑: 當一個命名爲my_module的模塊被導入時 解釋器首先會從內建模塊中尋找該名字 找不到,則去sys.path中找該名字 sys.path從如下位置初始化 執行文件所在的當前目錄 PTYHONPATH(包含一系列目錄名,與shell變量PATH語法同樣) 依賴安裝時默認指定的 注意:在支持軟鏈接的文件系統中,執行腳本所在的目錄是在軟鏈接以後被計算的,換句話說,包含軟鏈接的目錄不會被添加到模塊的搜索路徑中 在初始化後,咱們也能夠在python程序中修改sys.path,執行文件所在的路徑默認是sys.path的第一個目錄,在全部標準庫路徑的前面。這意味着,當前目錄是優先於標準庫目錄的,須要強調的是:咱們自定義的模塊名不要跟python標準庫的模塊名重複,除非你是故意的,傻叉。
爲了提升加載模塊的速度,強調強調強調:提升的是加載速度而絕非運行速度。python解釋器會在__pycache__目錄中下緩存每一個模塊編譯後的版本,格式爲:module.version.pyc。一般會包含python的版本號。例如,在CPython3.3版本下,my_module.py模塊會被緩存成__pycache__/my_module.cpython-33.pyc。這種命名規範保證了編譯後的結果多版本共存。
Python檢查源文件的修改時間與編譯的版本進行對比,若是過時就須要從新編譯。這是徹底自動的過程。而且編譯的模塊是平臺獨立的,因此相同的庫能夠在不一樣的架構的系統之間共享,即pyc使一種跨平臺的字節碼,相似於JAVA火.NET,是由python虛擬機來執行的,可是pyc的內容跟python的版本相關,不一樣的版本編譯後的pyc文件不一樣,2.5編譯的pyc文件不能到3.5上執行,而且pyc文件是能夠反編譯的,於是它的出現僅僅是用來提高模塊的加載速度的。
python解釋器在如下兩種狀況下不檢測緩存
1 若是是在命令行中被直接導入模塊,則按照這種方式,每次導入都會從新編譯,而且不會存儲編譯後的結果(python3.3之前的版本應該是這樣)
python -m my_module.py
2 若是源文件不存在,那麼緩存的結果也不會被使用,若是想在沒有源文件的狀況下來使用編譯後的結果,則編譯後的結果必須在源目錄下
提示:
1.模塊名區分大小寫,foo.py與FOO.py表明的是兩個模塊
2.你可使用-O或者-OO轉換python命令來減小編譯模塊的大小
-O轉換會幫你去掉assert語句 -OO轉換會幫你去掉assert語句和__doc__文檔字符串 因爲一些程序可能依賴於assert語句或文檔字符串,你應該在在確認須要的狀況下使用這些選項。
3.在速度上從.pyc文件中讀指令來執行不會比從.py文件中讀指令執行更快,只有在模塊被加載時,.pyc文件纔是更快的
4.只有使用import語句是纔將文件自動編譯爲.pyc文件,在命令行或標準輸入中指定運行腳本則不會生成這類文件,於是咱們可使用compieall模塊爲一個目錄中的全部模塊建立.pyc文件
模塊能夠做爲一個腳本(使用python -m compileall)編譯Python源 python -m compileall /module_directory 遞歸着編譯 若是使用python -O -m compileall /module_directory -l則只一層 命令行裏使用compile()函數時,自動使用python -O -m compileall 詳見:https://docs.python.org/3/library/compileall.html#module-compileall