1. 程式人生 > >python 包詳解

python 包詳解

orm 影響 voc class bit 分層 執行 絕對路徑 delay

包是一種管理 Python 模塊命名空間的形式,采用"點模塊名稱"。

比如一個模塊的名稱是 A.B, 那麽他表示一個包 A中的子模塊 B 。

就好像使用模塊的時候,你不用擔心不同模塊之間的全局變量相互影響一樣,采用點模塊名稱這種形式也不用擔心不同庫之間的模塊重名的情況。

這樣不同的作者都可以提供 NumPy 模塊,或者是 Python 圖形庫。

不妨假設你想設計一套統一處理聲音文件和數據的模塊(或者稱之為一個"包")。

現存很多種不同的音頻文件格式(基本上都是通過後綴名區分的,例如: .wav,:file:.aiff,:file:.au,),所以你需要有一組不斷增加的模塊,用來在不同的格式之間轉換。

並且針對這些音頻數據,還有很多不同的操作(比如混音,添加回聲,增加均衡器功能,創建人造立體聲效果),所以你還需要一組怎麽也寫不完的模塊來處理這些操作。

這裏給出了一種可能的包結構(在分層的文件系統中):

sound/                          頂層包
      __init__.py               初始化 sound 
      formats/                  文件格式轉換子包
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  聲音效果子包
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  filters 子包
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...

在導入一個包的時候,Python 會根據 sys.path 中的目錄來尋找這個包中包含的子目錄。

目錄只有包含一個叫做 __init__.py 的文件才會被認作是一個包,主要是為了避免一些濫俗的名字(比如叫做 string)不小心的影響搜索路徑中的有效模塊。

最簡單的情況,放一個空的 :file:__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語法會首先把item當作一個包定義的名稱,如果沒找到,再試圖按照一個模塊去導入。如果還沒找到,恭喜,一個:exc:ImportError 異常被拋出了。

反之,如果使用形如import item.subitem.subsubitem這種導入形式,除了最後一項,都必須是包,而最後一項則可以是模塊或者是包,但是不可以是類,函數或者變量的名字。


從一個包中導入*

設想一下,如果我們使用 from sound.effects import *會發生什麽?

Python 會進入文件系統,找到這個包裏面所有的子模塊,一個一個的把它們都導入進來。

但是很不幸,這個方法在 Windows平臺上工作的就不是非常好,因為Windows是一個大小寫不區分的系統。

在這類平臺上,沒有人敢擔保一個叫做 ECHO.py 的文件導入為模塊 echo 還是 Echo 甚至 ECHO。

(例如,Windows 95就很討厭的把每一個文件的首字母大寫顯示)而且 DOS 的 8+3 命名規則對長模塊名稱的處理會把問題搞得更糾結。

為了解決這個問題,只能煩勞包作者提供一個精確的包的索引了。

導入語句遵循如下規則:如果包定義文件 __init__.py 存在一個叫做 __all__ 的列表變量,那麽在使用 from package import * 的時候就把這個列表中的所有名字作為包內容導入。

作為包的作者,可別忘了在更新包之後保證 __all__ 也更新了啊。你說我就不這麽做,我就不使用導入*這種用法,好吧,沒問題,誰讓你是老板呢。這裏有一個例子,在:file:sounds/effects/__init__.py中包含如下代碼:

__all__ = ["echo", "surround", "reverse"]

這表示當你使用from sound.effects import *這種用法時,你只會導入包裏面這三個子模塊。

如果 __all__ 真的沒有定義,那麽使用from sound.effects import *這種語法的時候,就不會導入包 sound.effects 裏的任何子模塊。他只是把包sound.effects和它裏面定義的所有內容導入進來(可能運行__init__.py裏定義的初始化代碼)。

這會把 __init__.py 裏面定義的所有名字導入進來。並且他不會破壞掉我們在這句話之前導入的所有明確指定的模塊。看下這部分代碼:

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

這個例子中,在執行from...import前,包sound.effects中的echo和surround模塊都被導入到當前的命名空間中了。(當然如果定義了__all__就更沒問題了)

通常我們並不主張使用*這種方法來導入模塊,因為這種方法經常會導致代碼的可讀性降低。不過這樣倒的確是可以省去不少敲鍵的功夫,而且一些模塊都設計成了只能通過特定的方法導入。

記住,使用from Package import specific_submodule這種方法永遠不會有錯。事實上,這也是推薦的方法。除非是你要導入的子模塊有可能和其他包的子模塊重名。

如果在結構中包是一個子包(比如這個例子中對於包sound來說),而你又想導入兄弟包(同級別的包)你就得使用導入絕對的路徑來導入。比如,如果模塊sound.filters.vocoder 要使用包sound.effects中的模塊echo,你就要寫成 from sound.effects import echo。

from . import echo
from .. import formats
from ..filters import equalizer

無論是隱式的還是顯式的相對導入都是從當前模塊開始的。主模塊的名字永遠是"__main__",一個Python應用程序的主模塊,應當總是使用絕對路徑引用。

包還提供一個額外的屬性__path__。這是一個目錄列表,裏面每一個包含的目錄都有為這個包服務的__init__.py,你得在其他__init__.py被執行前定義哦。可以修改這個變量,用來影響包含在包裏面的模塊和子包。

這個功能並不常用,一般用來擴展包裏面的模塊。

python 包詳解