第五部分 模塊

模塊是最高級別的程序組織單元,它將程序代碼和數(shù)據(jù)封裝起來以便重用坛善。

模塊是變量名的封裝澎媒,被認為是命名空間搞乏。模塊導入時,模塊文件的全局作用域變成了模塊對象的命名空間旱幼。

在模塊文件頂層定義的所有變量名都變成了模塊對象的屬性查描。

模塊有三個角色:

  • 代碼重用
  • 系統(tǒng)命名空間的劃分
  • 實現(xiàn)共享服務和數(shù)據(jù)

1 模塊導入

在導入語句中的模塊名有兩個作用:

  • 識別需要加載的外部文檔
  • 賦值給被導入模塊的變量

模塊定義的對象會在import執(zhí)行時創(chuàng)建。

導入時運行時的運算柏卤,程序第一次導入指定文件時冬三,會執(zhí)行以下三個步驟:

  • 找到模塊文件
  • 編輯成字節(jié)碼(如果需要)
  • 執(zhí)行模塊代碼,創(chuàng)建其中定義的對象

這三個步驟只在程序執(zhí)行時缘缚,模塊第一次導入的時候執(zhí)行勾笆。在這之后導入相同模塊時會跳過這三個步驟,只是提取內存中已加載的模塊對象桥滨。

Python把加載的模塊存儲到sys.modules中窝爪,并在第一次導入時檢查該表。如果模塊不存在齐媒,則執(zhí)行這三個步驟蒲每。

1.1 搜索

Python的模塊搜索路徑由以下幾部分組成:

  • 程序的主目錄
  • PYTHONPATH環(huán)境變量的目錄(如果設置了的話)
  • 標準鏈接庫目錄
  • 任何.pth路徑文件中的內容(如果存在的話)

它們組合起來就是模塊搜索路徑sys.path。其中第一和第三項是自動定義的喻括,第二和第四項可以拓展搜索路徑邀杏。Python會從頭到尾搜索它們的組合,也就是從左到右搜索sys.path中的目錄唬血。

1.1.1 主目錄

Python首先在主目錄搜索導入的文件望蜡。主目錄的含義與如何運行代碼相關唤崭。運行一個程序時,主目錄是包含程序頂層腳本的目錄脖律;在交互式模式下谢肾,主目錄是當前的工作目錄。

因為Python總是優(yōu)先搜索這個目錄小泉,所以它會覆蓋其它目錄中的同名模塊芦疏,小心不要以這種方式覆蓋標準庫模塊。

1.1.2 PYTHONPATH目錄

之后膏孟,Python會從左到右搜索PYTHONPATH環(huán)境變量(如果設置了的話)中列出的目錄眯分。可以在該列表中包括所有想要導入的目錄柒桑,從而擴展模塊搜索路徑弊决。

只有導入的文件跨目錄時,即被導入的文件與進行導入的文件不在同一個目錄時魁淳,這個設置才顯得格外重要飘诗。

1.1.3 標準庫目錄

接著,Python會自動搜索標準庫模塊所在的目錄界逛。這些目錄一定會被搜索昆稿,所以不需要添加到PYTHONPATH或.pth路徑文件中。

1.1.4 .pth文件目錄

在后綴為.pth文件中一行一行列出目錄息拜,可以作為PYTHONPATH的一種替代方案溉潭。Python會把文件中每行羅列的目錄從頭到尾添加到模塊搜索路徑列表的最后。

Python可能會把當前工作目錄也加進來少欺,放在PYTHONPATH之后喳瓣,標準庫目錄之前。從命令行啟動程序時赞别,當前工作目錄和頂層文件的主目錄(也就是程序文件所在的目錄)不一定相同畏陕。每次執(zhí)行程序時,當前工作目錄可能會發(fā)生變化仿滔,所以不應該依賴這個值導入惠毁。

程序啟動時,Python自動將頂層文件的主目錄(或者指定當前工作目錄的空字符串)崎页,所有PYTHONPATH目錄鞠绰,標準庫目錄,以及已經(jīng)創(chuàng)建的.pth路徑文件中的目錄合并飒焦。

Python會選擇搜索路徑中第一個符合導入文件名的文件洞豁。import b語句可能會加載:

  • 源文件b.py
  • 字節(jié)碼文件b.pyc
  • 目錄b(包導入時)
  • 編譯后的擴展模塊(通常用C\C++編寫),導入時使用動態(tài)鏈接(比如.so.dll丈挟,或.pyd
  • 用C編寫的內置模塊,并使用靜態(tài)鏈接
  • ZIP文件組件志电,導入時自定解壓
  • 內存中的映像(對于fronzen可執(zhí)行文件)
  • Java類(Jython版本的Python中)
  • .NET組件(IronPython版本的Python中)

如果不同目錄中有b.pyb.so兩個文件曙咽,Python會從左到右搜索sys.path,并加載最先出現(xiàn)(最左邊)的文件挑辆。如果同一個目錄下有b.pyb.so例朱,則不一定加載哪個文件。

1.2 編譯

遍歷模塊搜索路徑鱼蝉,并找到源代碼文件后洒嗤,如果有必要,Python會將其編譯成字節(jié)碼文件魁亦。

Python比較字節(jié)碼文件和源文件的時間戳渔隶。如果字節(jié)碼文件比源文件舊,則會在程序運行時自動生成字節(jié)碼文件洁奈;否則會跳過編譯步驟间唉。

如果Python在搜索路徑上只發(fā)現(xiàn)了字節(jié)碼文件,而沒有源代碼文件利术,則會直接加載字節(jié)碼文件呈野。

當文件導入時會進行編譯。只有被導入的文件才會在機器上留下.pyc字節(jié)碼文件印叁,從而提高之后的導入速度被冒。頂層文件的字節(jié)碼文件在內部使用后就丟棄了。

1.3 運行

import操作最后的步驟是執(zhí)行模塊的字節(jié)碼轮蜕。文件中的所有語句會被依次執(zhí)行昨悼。在這個步驟中,對任何變量名的賦值運算都會產生模塊文件的屬性肠虽。

2 模塊使用

在Python 3中幔戏,from *語句只能用于模塊文件的頂層,而不能用于函數(shù)中税课。

importfrom都是隱性的賦值語句:

  • import將整個模塊對象賦值給一個對象名
  • from將一個或多個變量名賦值給另一個模塊中的同名對象

from復制的變量名會變成對共享對象的引用闲延。與函數(shù)參數(shù)一樣,對已取出的變量名重新賦值韩玩,不會影響其復制之處的模塊垒玲;但是修改一個已取出的可變對象,則會影響導入模塊內的對象找颓。例如文件small.py

x = 1
y = [1, 2]

% python
>>> from small import x, y
>>> x = 42
>>> y[0] = 42

這里x并不是一個共享的可變對象合愈,但y是。導入者中的變量名y和被導入者都引用了同一個列表對象。所以在其中一個地方修改佛析,也會影響另一個地方的對象益老。

>>> import small
>>> small.x
1
>>> small.y
[42, 2]

from復制而來的變量名與其來源的文件之間沒有聯(lián)系。要實際修改另一個文件中的全局變量名寸莫,必須使用import捺萌。

% python
>>> from small import x, y
>>> x = 42    # 只修改了本地的x

>>> import small
>>> small.x = 42   # 修改了另一個模塊中的x

from的第一步也是普通的導入操作,它總是會把整個模塊導入內容(如果還沒有導入的話)膘茎。

3 模塊命名空間

在模塊文件頂層(而不是函數(shù)或者類的主體內)每一個賦值了的變量名都會變成該模塊的屬性桃纯。

模塊的命名空間可以通過__dict__屬性或dir(M)獲取。

變量的含義一定是由源代碼中的賦值語句的位置決定的披坏。

4 包導入

Python首次導入某個目錄時态坦,會自動執(zhí)行該目錄下__init__.py文件中的所有代碼。所以棒拂,可以在該文件內放置包內文件所需要的初始化代碼伞梯。

可以在__init__.py文件中使用__all__列表,在其中定義目錄以from *語句導入時需要導出的子模塊的名稱着茸。如果沒有設置__all__壮锻,則from *語句不會自動加載嵌套與該目錄內的子模塊。

4.1 包相對導入

Python 3中包導入的變化:

  1. 修改了模塊導入時搜索路徑的語義涮阔,它默認會跳過包自身的目錄猜绣,只檢查搜索路徑的其它組件,這叫“絕對”導入敬特。
  2. 擴展了from語法掰邢,允許顯式地要求導入只搜索包自身的目錄,這叫“相對”導入伟阔。

from語句使用點號導入位于同一個包中的模塊(相對導入)辣之,而不是導入位于模塊搜索路徑上某處的模塊(絕對導入):

  1. 只在包內搜索,不會搜索模塊搜索路徑上某處的同名文件皱炉。直接效果就是包模塊覆蓋了外部的模塊怀估。
  2. 在Python 3中,包代碼的常規(guī)導入(前面不含點號)默認是絕對的——忽略包含包自身的目錄合搅,只在sys.path上的目錄搜索多搀。

相對導入適用于只在包內導入,并且只能用于from語句灾部。

Python 3中的模塊查找規(guī)則:

  1. 簡單模塊名(import A)通過搜索sys.path列表中的每個目錄查找康铭。
  2. 包是帶有__init__.py文件的模塊目錄,它可以使用A.B.C語法赌髓。其中A目錄位于sys.path搜索路徑中从藤。
  3. 在一個包內的模塊文件中催跪,常規(guī)的import語句使用sys.path搜索規(guī)則。如果包內的模塊文件使用前面帶點號的from語句時夷野,則它相對于包導入懊蒸。也就是說,只檢查包目錄悯搔,并不使用sys.path常規(guī)查找榛鼎。

5 其它

5.1 最小化from *的破壞

在變量前加下劃線,可以防止客戶端使用from *語句導入模塊時鳖孤,把其中的變量名復制出去。也可以在模塊頂層把變量名的字符串列表賦值給__all__變量抡笼。

5.2 用名稱字符串導入模塊

有兩種方式可以用模塊的字符串名稱導入模塊:

  1. 把一條導入語句構建為一個字符串苏揣,并將它傳遞給exec()內置函數(shù).
  2. 使用內置函數(shù)__import__()
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末推姻,一起剝皮案震驚了整個濱河市平匈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌藏古,老刑警劉巖增炭,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異拧晕,居然都是意外死亡隙姿,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門厂捞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來输玷,“玉大人,你說我怎么就攤上這事靡馁∮簦” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵臭墨,是天一觀的道長赔嚎。 經(jīng)常有香客問我,道長胧弛,這世上最難降的妖魔是什么尤误? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮叶圃,結果婚禮上袄膏,老公的妹妹穿的比我還像新娘。我一直安慰自己掺冠,他們只是感情好沉馆,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布码党。 她就那樣靜靜地躺著,像睡著了一般斥黑。 火紅的嫁衣襯著肌膚如雪揖盘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天锌奴,我揣著相機與錄音兽狭,去河邊找鬼。 笑死鹿蜀,一個胖子當著我的面吹牛箕慧,可吹牛的內容都是我干的。 我是一名探鬼主播茴恰,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼颠焦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了往枣?” 一聲冷哼從身側響起伐庭,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎分冈,沒想到半個月后圾另,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡雕沉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年集乔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蘑秽。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡饺著,死狀恐怖,靈堂內的尸體忽然破棺而出肠牲,到底是詐尸還是另有隱情幼衰,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布缀雳,位于F島的核電站渡嚣,受9級特大地震影響,放射性物質發(fā)生泄漏肥印。R本人自食惡果不足惜识椰,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望深碱。 院中可真熱鬧腹鹉,春花似錦、人聲如沸敷硅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至力奋,卻和暖如春榜旦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背景殷。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工溅呢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人猿挚。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓咐旧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親绩蜻。 傳聞我的和親對象是個殘疾皇子休偶,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內容