模塊是用來組織 Python 代碼的方法,而包則是用來組織模塊的。
當代碼量很大時,我們一般會把代碼分成幾個有組織的代碼段,然后每個代碼段之間有一定的聯(lián)系。代碼單之間是共享的,所以Python允許調入一個模塊,允許使用其他模塊的屬性利用之前的工作成果,實現(xiàn)代碼重用。那些自我包含并且有組織的代碼片段就是模塊(module),將其他模塊中屬性附加到你的模塊中的操作較導入(import)
模塊是邏輯上的說法,而它們在物理層是一個個獨立的文件,模塊的文件名就是模塊的名字加拓展名.py。與其他可以導入類的語言不同,Python中你導入的是模塊或模塊屬性。
模塊的名稱空間
模塊名稱是他們屬性名稱中的一個重要部分,給定一個模塊名后,只可能有一個模塊被導入到Python解釋器中,所以不同模塊間不能出現(xiàn)名稱交叉現(xiàn)象。如果在模塊mymodule里創(chuàng)建了一個atoi()函數(shù),那么它的名字應該是 mymodule.atoi()。所以即使屬性之間有名稱沖突,但他們的完整授權名稱通過句點屬性標識指定了各自的名稱空間,防止名稱沖突產(chǎn)生。
模塊的導入需要一個路徑搜索的過程。即在文件的“預定義區(qū)域”中查找 mymodule.py 文件(如果導入的是mymodule的話)。這些預定義區(qū)域不過是你的 Python 搜索路徑的集合。路徑搜索和搜索路徑概念不同,前者指查找某個文件,后者是去查找一組目錄。
默認的搜索路徑是在編譯或是安裝時指定的,它可以在一個或兩個地方修改。
一個是啟動Python的shell命令行的PYTHONPATH環(huán)境變量。該變量的內容是一組用冒號分隔的目錄路徑。如果你想讓解釋器使用這個變量,請確保在啟動解釋器或執(zhí)行Python腳本前設置或修改了該變量。
解釋器啟動之后,也可以訪問這個搜索路徑,它會被保存在 sys 模塊的 sys.path 變量里。
不過它已經(jīng)不是冒號分隔的字符串,而是包含每個獨立路徑的列表。
這只是個列表,所以可以隨時隨地對它修改,如果需要知道你要導入的模塊是什么,而它的路徑不再搜索路徑里,那么只需調用append()方法即可。
修改完成后,你就可以加載自己的模塊了。只要列表中的某個目錄包含這個而文件,它就會被正確導入。
在同一個模塊的多個拷貝都出現(xiàn)在路徑中時,會使用沿用搜索路徑找到的第一個模塊。
名稱空間
名稱空間是名稱(標識符)到對象的映射。向名稱空間添加名稱的操作過程涉及到綁定標識符到指定對象的操作(以及給該對象的引用計數(shù)加1)。
Python在執(zhí)行期間由兩個或三個活動的名稱空間,分別是局部名稱空間,全局名稱空間和內建名稱空間。
Python解釋器會首先加載內建名稱空間,它由 __builtins__ 模塊的名字構成。隨后加載執(zhí)行模塊的全局名稱空間,他會在模塊開始執(zhí)行后變?yōu)榛顒用Q空間。如果在執(zhí)行期間調用了一個函數(shù),那么將創(chuàng)建出第三個名稱空間,局部名稱空間。
關于__builtins__ 和 __builtin__
現(xiàn)在盡可能只使用 __builtins__ ,不使用__builtin__ ,現(xiàn)在所有__builtin__的內容都在__builtins__里有,而__builtin__已經(jīng)不太支持。
名稱空間與變量作用域的關系
名稱空間是純粹意義上的名字和對象間的映射關系,而作用域還指出了從用戶代碼的那些物理位置可以訪問到這些名字。
名稱查找、確定作用域和覆蓋
名稱查詢將確定作用域的規(guī)則覆蓋到名稱空間。訪問一個屬性時,解釋器必須在三個名稱空間中的一個里找到它,先從局部名稱空間開始,如果沒有找到,解釋器會繼續(xù)查找全局名稱空間,如果失敗了,將在內建名稱空間里查找。如果都沒找到,返回一個NameError。
這種先后查找的方式是遮蔽了之后的名稱空間。也就是局部變量覆蓋了全局變量:
def foo(): PRint "calling foo()..." bar = 200 print "in foo(), bar is",barbar = 100print "in __main__, bar is", barfoo()print "in __main__, bar is", bar
結果是:
>>> in __main__, bar is 100calling foo()...in foo(), bar is 200in __main__, bar is 100
如果如下代碼:
def foo(): print "calling foo()..." print "before we assign bar,bar is",bar bar = 200 print "in foo(), bar is",barbar = 100print "in __main__, bar is", barfoo()print "in __main__, bar is", bar
則返回:
>>> in __main__, bar is 100calling foo()...before we assign bar,bar isTraceback (most recent call last):File "F:/桌面/PythonProgram/namespace.py", line 8, in <module>foo()File "F:/桌面/PythonProgram/namespace.py", line 3, in fooprint "before we assign bar,bar is",barUnboundLocalError: local variable 'bar' referenced before assignment
如果刪除其中一行:
def foo(): print "calling foo()..." print "before we assign bar,bar is",bar print "in foo(), bar is",barbar = 100print "in __main__, bar is", barfoo()print "in __main__, bar is", bar
則結果為:
>>> in __main__, bar is 100calling foo()...before we assign bar,bar is 100in foo(), bar is 100in __main__, bar is 100
上面的嘗試里,反映出了局部名稱空間覆蓋了全局名稱空間的過程。當我在函數(shù)中給bar賦值后,bar變成局部變量,也就是出現(xiàn)在局部名稱空間里,此時在給bar賦值前調用它就會報錯。
導入模塊
導入語句有兩種:
import module1import module2...或import module1,module2,...
這兩種沒有太大的不同,只有可讀性的差別。
核心風格:一般我們推薦所有的模塊在開頭導入,最好按這樣的順序
Python標準庫模塊
Python第三方模塊
應用程序自定義模塊
然后用一個空行分割這三個模塊的導入語句
from-import語句
這是導入模塊的指定屬性。也就是把指定名稱導入到當前作用域,語法如下:
from module import name1[,name2,...]
導入內容過長變成多行導入時,用一個/可以換行。
拓展import語句(as)
當你想使用一個模塊或者模塊屬性,但想改一個名字時,可以使用自己要的名字替換模塊的原始名字,語法如下:
import longmodulename as yourname
from-import語句可以把名字導入到當前空間去,不需要在使用時加句點屬性標識符,如果要把所有的名稱都導入到當前名稱空間可以用:
from module import *
當然,從實踐中來看,這不是很好的編程風格,它污染了當前名稱空間,很可能覆蓋當前名稱空間現(xiàn)有的名字。
模塊內建函數(shù)
__import__()
這是import的實際函數(shù),我們使用它來完成導入工作,而提供這個函數(shù)是為了讓有特殊需求的用戶來覆蓋它,實現(xiàn)自定義導入。
__import__()語法是:
__import__(module_name[,globals[, locals[, fromlist[]]]])module_name 是導入模塊的名字,globals 是包含當前全局符號表的名字的字典, locals 是包含局部符號表的名字的字典, fromlist是一個使用from-import 語句所導入符號的列表。
globals() 和 locals()
這兩個內建函數(shù)分別返回調用者全局和局部名稱空間的字典。
在全局名稱空間下,globals() 和 locals() 返回相同的字典,因為局部名稱空間就是全局空間。
reload()
這個內建函數(shù)可以重新導入一個已經(jīng)導入的模塊。語法是:
reload(module)
模塊中的代碼在導入時被執(zhí)行,但只執(zhí)行一次。以后執(zhí)行 import 語句不會再次執(zhí)行這些代碼,只是綁定模塊名稱。而 reload()可以多次執(zhí)行。
包
包是一個有層次的文件目錄結構,它定義了一個由模塊和子包組成的 Python 應用程序執(zhí)行環(huán)境。
可以解決如下問題:
為平坦的名稱空間加入有層次的組織結構
允許程序員把有聯(lián)系的模塊組合到一起
允許分發(fā)者使用目錄結構而不是一大堆混亂的文件
幫助解決有沖突的模塊名稱
包也使用句點屬性標識來訪問他們的元素。
假設有這樣的包
Phone/
__init__.py
common_util.py
Voicedta/
__init__.py
Pots.py
Isdn.py
Fax/
__init__.py
G3.py
Mobile/
__init__.py
Analog.py
digital.py
Pager/
__init__.py
Numeric.py
可以這樣導入包:
import Phone.Mobile.AnalogPhone.Mobile.Analog.dial()或from Phone import MobileMobile.Analog.dial()或from Phone.Mobile import AnalogAnalog.dial()或from Phone.Mobile.Analog import dialdial()
上面的目錄結構中有很多__init__.py文件。這是初始化模塊,導入子包時from-import語句要用到它。如果沒有用到,他們可以是空文件。
絕對導入
因為包的使用越來越廣發(fā),很多情況下導入子包會和標準庫模塊發(fā)生沖突,包模塊會把名字相同的標準庫模塊隱藏掉。為此,所有的導入現(xiàn)在都被認為是絕對的,也就是說這些名字必須通過Python路徑來訪問。
當然,也留下了一定的相對導入操作,第一部分是句點,表示相對導入,然后用附加句點表示在哪個位置級別。
如上的目錄結構,如果我們在Digital.py中,有以下的導入方法:
from Phone.Mobile.Analog import dial #絕對導入from .Analog import dialfrom ..common_util import setupform ..Fax import G3.dial
新聞熱點
疑難解答