python基礎教程:包的建立及導入

包是一種經過用「帶點號的模塊名」來構造 Python 模塊命名空間的方法。 例如,模塊名 A.B 表示 A 包中名爲 B 的子模塊。正如模塊的使用使得不一樣模塊的做者沒必要擔憂彼此的全局變量名稱同樣,使用加點的模塊名可使得 NumPy 或 Pillow 等多模塊軟件包的做者沒必要擔憂彼此的模塊名稱同樣。html

假設你想爲聲音文件和聲音數據的統一處理,設計一個模塊集合(一個「包」)。因爲存在不少不一樣的聲音文件格式(一般由它們的擴展名來識別,例如:.wav, .aiff, .au),所以爲了避免同文件格式間的轉換,你可能須要建立和維護一個不斷增加的模塊集合。 你可能還想對聲音數據還作不少不一樣的處理(例如,混聲,添加回聲,使用均衡器功能,創造人工立體聲效果), 所以爲了實現這些處理,你將另外寫一個無窮盡的模塊流。這是你的包的可能結構(以分層文件系統的形式表示):python

      __init__.py               Initialize the sound package
      formats/                  Subpackage for file format conversions
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  Subpackage for sound effects
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  Subpackage for filters
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...

 

當導入這個包時,Python搜索 sys.path 裏的目錄,查找包的子目錄。函數

The init__.py files are required to make Python treat directories containing the file as packages. This prevents directories with a common name, such as string, unintentionally hiding valid modules that occur later on the module search path. In the simplest case, __init__.py can just be an empty file, but it can also execute initialization code for the package or set the __all variable, described later.測試

包的用戶能夠從包中導入單個模塊,例如:ui

這會加載子模塊 sound.effects.echo 。但引用它時必須使用它的全名。spa

導入子模塊的另外一種方法是設計

這也會加載子模塊 echo ,並使其在沒有包前綴的狀況下可用,所以能夠按以下方式使用:code

另外一種形式是直接導入所需的函數或變量:orm

一樣,這也會加載子模塊 echo,但這會使其函數 echofilter() 直接可用:htm

請注意,當使用 from package import item 時,item能夠是包的子模塊(或子包),也能夠是包中定義的其餘名稱,如函數,類或變量。 import 語句首先測試是否在包中定義了item;若是沒有,它假定它是一個模塊並嘗試加載它。若是找不到它,則引起 ImportError 異常。

相反,當使用 import item.subitem.subsubitem 這樣的語法時,除了最後一項以外的每一項都必須是一個包;最後一項能夠是模塊或python包,但不能是前一項中定義的類或函數或變量。

從包中導入

當用戶寫 from sound.effects import * 會發生什麼?理想狀況下,人們但願這會以某種方式傳遞給文件系統,找到包中存在哪些子模塊,並將它們所有導入。這可能須要很長時間,導入子模塊可能會產生沒必要要的反作用,這種反作用只有在顯式導入子模塊時纔會發生。

惟一的解決方案是讓包做者提供一個包的顯式索引。import 語句使用下面的規範:若是一個包的 init__.py 代碼定義了一個名爲 __all 的列表,它會被視爲在遇到 from package import 時應該導入的模塊名列表。在發佈該包的新版本時,包做者能夠決定是否讓此列表保持更新。包做者若是認爲從他們的包中導入 的操做沒有必要被使用,也能夠決定不支持此列表。例如,文件 sound/effects/__init__.py 能夠包含如下代碼:

這意味着 from sound.effects import * 將導入 sound 包的三個命名子模塊。

若是沒有定義 __all__,from sound.effects import * 語句 不 會從包 sound.effects 中導入全部子模塊到當前命名空間;它只確保導入了包 sound.effects (可能運行任何在 __init__.py 中的初始化代碼),而後導入包中定義的任何名稱。這包括 __init__.py` 定義的任何名稱(以及顯式加載的子模塊)。它還包括由以前的 import 語句顯式加載的包的任何子模塊。思考下面的代碼:

import sound.effects.surround
from sound.effects import *

 

在這個例子中, echo 和 surround 模塊是在執行 from...import 語句時導入到當前命名空間中的,由於它們定義在 sound.effects 包中。(這在定義了 all 時也有效。)

雖然某些模塊被設計爲在使用 import * 時只導出遵循某些模式的名稱,但在生產代碼中它仍然被認爲是很差的作法。

請記住,使用 from Package import specific_submodule 沒有任何問題!實際上,除非導入模塊須要使用來自不一樣包的同名子模塊,不然這是推薦的表示法。

子包參考

當包被構形成子包時(與示例中的 sound 包同樣),你可使用絕對導入來引用兄弟包的子模塊。例如,若是模塊 sound.filters.vocoder 須要在 sound.effects 包中使用 echo 模塊,它可使用 from sound.effects import echo 。

你還可使用import語句的 from module import name 形式編寫相對導入。這些導入使用前導點來指示相對導入中涉及的當前包和父包。例如,從 surround 模塊,你可使用:

from .. import formats
from ..filters import equalizer

 

請注意,相對導入是基於當前模塊的名稱進行導入的。因爲主模塊的名稱老是 "__main__" ,所以用做Python應用程序主模塊的模塊必須始終使用絕對導入。

多個目錄中的包
包支持另外一個特殊屬性, path 。它被初始化爲一個列表,其中包含在執行該文件中的代碼以前保存包的文件 __init__.py 的目錄的名稱。這個變量能夠修改;這樣作會影響未來對包中包含的模塊和子包的搜索。

雖然一般不須要此功能,但它可用於擴展程序包中的模塊集。

腳註

[1] 實際上,函數定義也是「被執行」的「語句」;模塊級函數定義的執行在模塊的全局符號表中輸入該函數名。

相關文章
相關標籤/搜索