包通常是使用用“圓點模塊名”的結構化模塊命名空間。例如,名為?A.B?的模塊表示了名為?A?的包中名為?B?的子模塊。正如同用模塊來保存不同的模塊架構可以避免全局變量之間的相互沖突,使用圓點模塊名保存像 NumPy 或 Python Imaging Library 之類的不同類庫架構可以避免模塊之間的命名沖突。
假設你現在想要設計一個模塊集(一個“包”)來統一處理聲音文件和聲音數據。存在幾種不同的聲音格式(通常由它們的擴展名來標識,例如:.wav,?.aiff,.au?),于是,為了在不同類型的文件格式之間轉換,你需要維護一個不斷增長的包集合。可能你還想要對聲音數據做很多不同的操作(例如混音,添加回聲,應用平衡 功能,創建一個人造效果),所以你要加入一個無限流模塊來執行這些操作。你的包可能會是這個樣子(通過分級的文件體系來進行分組):
~~~
sound/ Top-level package
__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?搜索路徑查找包含這個包的子目錄。
為了讓 Python 將目錄當做內容包,目錄中必須包含?__init__.py?文件。這是為了避免一個含有爛俗名字的目錄無意中隱藏了稍后在模塊搜索路徑中出現的有效模塊,比如 string。最簡單的情況下,只需要一個空的?__init__.py?文件即可。當然它也可以執行包的初始化代碼,或者定義稍后介紹的__all__?變量。
用戶可以每次只導入包里的特定模塊,例如:
`import sound.effects.echo`
這樣就導入了?sound.effects.echo?子模塊。它必需通過完整的名稱來引用:
~~~
sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
~~~
導入包時有一個可以選擇的方式:
~~~
from sound.effects import echo
~~~
這樣就加載了?echo?子模塊,并且使得它在沒有包前綴的情況下也可以使用,所以它可以如下方式調用:
`echo.echofilter(input, output, delay=0.7, atten=4)`
還有另一種變體用于直接導入函數或變量:
~~~
from sound.effects.echo import echofilter
~~~
這樣就又一次加載了?echo?子模塊,但這樣就可以直接調用它的?echofilter()?函數:
~~~
echofilter(input, output, delay=0.7, atten=4)
~~~
需要注意的是使用?from?package?import?item?方式導入包時,這個子項(item)既可以是包中的一個子模塊(或一個子包),也可以是包中定義的其它命名,像函數、類或變量。import?語句首先核對是否包中有這個子項,如果沒有,它假定這是一個模塊,并嘗試加載它。如果沒有找到它,會引發一個?ImportError?異常。
相反,使用類似?import?item.subitem.subsubitem?這樣的語法時,這些子項必須是包,最后的子項可以是包或模塊,但不能是前面子項中定義的類、函數或變量。
### 6.4.1\. 從 * 導入包
那么當用戶寫下?from?sound.Effects?import?*?時會發生什么事?理想中,總是希望在文件系統中找出包中所有的子模塊,然后導入它們。這可能會花掉委有長時間,并且出現期待之外的邊界效應,導出了希望只能顯式導入的包。
對于包的作者來說唯一的解決方案就是給提供一個明確的包索引。import?語句按如下條件進行轉換:執行?from?package?import?*?時,如果包中的?__init__.py?代碼定義了一個名為?__all__?的列表,就會按照列表中給出的模塊名進行導入。新版本的包發布時作者可以任意更新這個列表。如果包作者不想 import * 的時候導入他們的包中所有模塊,那么也可能會決定不支持它( import * )。例如,?sounds/effects/__init__.py?這個文件可能包括如下代碼:
~~~
__all__ = ["echo", "surround", "reverse"]
~~~
這意味著?from?Sound.Effects?import?*?語句會從?sound?包中導入以上三個已命名的子模塊。
如果沒有定義?__all__?,?from?Sound.Effects?import?*?語句?_不會_?從?sound.effects?包中導入所有的子模塊。無論包中定義多少命名,只能確定的是導入了?sound.effects?包(可能會運行__init__.py?中的初始化代碼)以及包中定義的所有命名會隨之導入。這樣就從?__init__.py?中導入了每一個命名(以及明確導入的子模塊)。同樣也包括了前述的?import?語句從包中明確導入的子模塊,考慮以下代碼:
~~~
import sound.effects.echo
import sound.effects.surround
from sound.effects import *
~~~
在這個例子中,echo?和?surround?模塊導入了當前的命名空間,這是因為執行?from...import?語句時它們已經定義在?sound.effects?包中了(定義了?__all__?時也會同樣工作)。
盡管某些模塊設計為使用?import?*?時它只導出符全某種模式的命名,仍然不建議在生產代碼中使用這種寫法。
記住,from?Package?import?specific_submodule?沒有錯誤!事實上,除非導入的模塊需要使用其它包中的同名子模塊,否則這是推薦的寫法。
### 6.4.2\. 包內引用
如果包中使用了子包結構(就像示例中的?sound?包),可以按絕對位置從相鄰的包中引入子模塊。例如,如果?sound.filters.vocoder?包需要使用?sound.effects?包中的?echo?模塊,它可以from?Sound.Effects?import?echo。
你可以用這樣的形式?from?module?import?name?來寫顯式的相對位置導入。那些顯式相對導入用點號標明關聯導入當前和上級包。以?surround?模塊為例,你可以這樣用:
~~~
from . import echo
from .. import formats
from ..filters import equalizer
~~~
需要注意的是顯式或隱式相對位置導入都基于當前模塊的命名。因為主模塊的名字總是"__main__",Python 應用程序的主模塊應該總是用絕對導入。
### 6.4.3\. 多重目錄中的包
包支持一個更為特殊的特性,?__path__。 在包的?__init__.py?文件代碼執行之前,該變量初始化一個目錄名列表。該變量可以修改,它作用于包中的子包和模塊的搜索功能。
這個功能可以用于擴展包中的模塊集,不過它不常用。
Footnotes
| [[1]](http://www.pythondoc.com/pythontutorial3/modules.html#id3) | 事實上函數定義既是“聲明”又是“可執行體”;執行體由函數在模塊全局語義表中的命名導入。 |
- Python 入門指南
- 1. 開胃菜
- 2. 使用 Python 解釋器
- 2.1. 調用 Python 解釋器
- 2.2. 解釋器及其環境
- 3. Python 簡介
- 3.1. 將 Python 當做計算器
- 3.2. 編程的第一步
- 4. 深入 Python 流程控制
- 4.1. if 語句
- 4.2. for 語句
- 4.3. range() 函數
- 4.4. break 和 continue 語句, 以及循環中的 else 子句
- 4.5. pass 語句
- 4.6. 定義函數
- 4.7. 深入 Python 函數定義
- 4.8. 插曲:編碼風格
- 5. 數據結構
- 5.1. 關于列表更多的內容
- 5.2. del 語句
- 5.3. 元組和序列
- 5.4. 集合
- 5.5. 字典
- 5.6. 循環技巧
- 5.7. 深入條件控制
- 5.8. 比較序列和其它類型
- 6. 模塊
- 6.1. 深入模塊
- 6.2. 標準模塊
- 6.3. dir() 函數
- 6.4. 包
- 7. 輸入和輸出
- 7.1. 格式化輸出
- 7.2. 文件讀寫
- 8. 錯誤和異常
- 8.1. 語法錯誤
- 8.2. 異常
- 8.3. 異常處理
- 8.4. 拋出異常
- 8.5. 用戶自定義異常
- 8.6. 定義清理行為
- 8.7. 預定義清理行為
- 9. 類
- 9.1. 術語相關
- 9.2. Python 作用域和命名空間
- 9.3. 初識類
- 9.4. 一些說明
- 9.5. 繼承
- 9.6. 私有變量
- 9.7. 補充
- 9.8. 異常也是類
- 9.9. 迭代器
- 9.10. 生成器
- 9.11. 生成器表達式
- 10. Python 標準庫概覽
- 10.1. 操作系統接口
- 10.2. 文件通配符
- 10.3. 命令行參數
- 10.4. 錯誤輸出重定向和程序終止
- 10.5. 字符串正則匹配
- 10.6. 數學
- 10.7. 互聯網訪問
- 10.8. 日期和時間
- 10.9. 數據壓縮
- 10.10. 性能度量
- 10.11. 質量控制
- 10.12. “瑞士軍刀”
- 11. 標準庫瀏覽 – Part II
- 11.1. 輸出格式
- 11.2. 模板
- 11.3. 使用二進制數據記錄布局
- 11.4. 多線程
- 11.5. 日志
- 11.6. 弱引用
- 11.7. 列表工具
- 11.8. 十進制浮點數算法
- 12. 接下來?
- 13. 交互式輸入行編輯歷史回溯
- 13.1. 行編輯
- 13.2. 歷史回溯
- 13.3. 快捷鍵綁定
- 13.4. 其它交互式解釋器
- 14. 浮點數算法:爭議和限制
- 14.1. 表達錯誤
- 15. 附錄
- 15.1. 交互模式