Python手冊模組

來源:互聯網
上載者:User

[譯]The Python Tutorial#Modules

6. Modules

如果你從Python解譯器中退出然後重新進入,之前定義的名字(函數和變數)都丟失了。因此,如果你想寫長一點的程式,使用文字編輯器來準備解譯器的輸入會更好,使用檔案作為替代的輸入。這也被稱作建立指令碼。當程式越來越長時,出於易於維護的原因,你可能會將程式分割為幾個檔案。你也可能想要在多個程式中使用很好用的一個函數,而不用將其定義拷貝到每一個程式中。

為了支援這些需求,Python提供了將定義放入一個檔案的方式,並且在指令碼或者解譯器互動式執行個體中使用它們。這樣的檔案稱為模組;模組中的定義可以匯入到其他模組或者主模組中(在頂層執行的指令碼和計算模式中可訪問到的變數集合)。

模組就是一個包含Python定義和語句的檔案。檔案名稱是模組名並且帶有.py尾碼。在模組中,模組的名字(作為字串),作為全域變數__name__的值,是可用的。例如,使用你最喜歡的文字編輯器在目前的目錄建立fibo.py檔案,內容如下:

# Fibonacci numbers moduledef fib(n):    # write Fibonacci series up to na, b = 0, 1while b < n:print(b, end=' ')        a, b = b, a+bprint()def fib2(n):   # return Fibonacci series up to nresult = []    a, b = 0, 1while b < n:        result.append(b)        a, b = b, a+breturn result

進入Python解譯器,使用下列命令匯入這個模組:

>>> import fibo

這個操作並不會講fibo中定義的函數的名字匯入到當前符號表中;只是匯入模組的名字fibo。使用模組名字可訪問到函數:

>>> fibo.fib(1000)1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987>>> fibo.fib2(100)[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]>>> fibo.__name__'fibo'

如果想要頻繁使用函數,可以將其賦值給局部名字:

>>> fib = fibo.fib>>> fib(500)1 1 2 3 5 8 13 21 34 55 89 144 233 377

6.1 More on Modules

模組可以同時包含可執行語句和函數定義。可執行語句用來初始化模組。當模組名字出現在匯入語句中時,這些可執行語句只執行一次[1]。(如果檔案作為指令碼,這些可執行也會執行)

每一個模組都有自己私人的符號表,這個符號表被所有定義在模組中的函數作為全域符號表使用。因此,模組的作者可以使用這些全域變數,而不用擔心和用於全域變數偶然的名字衝突。另一方面,如果你知道自己在做什麼,你可以使用與引用函數相同的方法來引用模組的全域變數,modname.itemname

模組可以引用其他模組。將所有import語句放置到模組的開始處(或者指令碼)是一個很好的習慣,但並不是強制的。被匯入的模組名字將會被放置到當前模組的全域符號表中。

有一種匯入語句的變種方法,將模組的名字直接匯入到當前模組的符號表中。例如:

>>> from fibo import fib, fib2>>> fib(500)1 1 2 3 5 8 13 21 34 55 89 144 233 377

以上並不會引入模組的名字(在上面的例子中fibo不會被定義)。

甚至有一種方法可以匯入模組中定義的所有名字:

>>> from fibo import *>>> fib(500)1 1 2 3 5 8 13 21 34 55 89 144 233 377

這種方法匯入所有不以底線_開頭的名字。大多數情況下,Python程式員不會使用這種方法,因為這會匯入未知的名字集合到解譯器中,也許還會屏蔽已經定義的一些名字。

需要注意,通常在實踐中從模組或者包中匯入所以名字是不鼓勵使用的,因為會降低程式的易讀性。然而,在互動式環境中使用它來減少打字輸入是可行的。

注意: 出於效能原因,一個解譯器會話中每個模組只匯入一次。因此,如果模組被改變了,必須重啟解譯器;如果只想要互動式測試一個模組,使用 importlib.reload(),例如:import importlib; importlin.reload(modulename)

6.1.1 Executing modules as scripts

當使用以下命令運行Python模組:

python fibo.py <arguments>

模組中的代碼會被執行,就像匯入該模組一樣,但是這時__name__被設定為__main__。這意味著以下代碼會加入到模組末尾:

if __name__ == "__main__":import sys    fib(int(sys.argv[1]))

模組即作為指令碼執行,也可以作為模組匯入,因為只有當模組作為mian檔案執行時候,解析命令列的代碼才會執行:

$ python fibo.py 501 1 2 3 5 8 13 21 34

如果模組被匯入,代碼不會執行:

>>> import fibo>>>

這可以用來為使用者提供一個模組使用者介面的使用約定,也可以用作測試(模組作為指令碼時執行測試案例)

6.1.2 The Module Search Path

當模組spam被匯入時,解譯器首先搜尋built-in模組。如果沒有找到,解譯器在變數sys.path提供的路徑列表中搜尋名為spam.py的檔案。sys.path從下列位置初始化:

  • 包含輸入指令碼的目錄(或者沒有指定檔案時的目前的目錄)

  • PYTHONPATH(目錄名字集合,與shell環境變數PATH相似,也是環境變數)

  • 安裝預設目錄

注意: 在支援符號連結的檔案系統,包含輸入指令碼的目錄是符號連結指向的目錄。也就是說包含符號連結的目錄不會被加入到搜尋路徑中。

初始化後,Python程式可以修改sys.path。包含執行指令碼的目錄被放置到搜尋路徑的開始,在標準庫路徑之前。這意味著該目錄中的指令碼會被載入,而標準庫目錄中的同名模組不會被載入。這會引發錯誤,除非是有意替換標準庫的模組。閱讀Standard Modules擷取更多資訊。 (譯註:自訂的模組不應與標準模組重名,否則標準模組會被覆蓋。)

6.1.3 “Compiled” Python files

為加速模組載入,Python會在__pycache__目錄中緩衝每個模組的編譯版本,快取檔案名為module.version.pycversion編碼了被編譯檔案的版本;通常包含了Python的版本號碼。例如,在CPython release 3.3中,檔案spam.py的編譯版本會被緩衝為__pycache__/spam.cpython-33.pyc。這種命名規範允許來自不同Python發行版本的模組得以共存。

Python檢查源檔案的修改日期與編譯版本,來確定編譯版本是否到期,是否需要重新編譯。這是一個完全自動化的過程。另外,編譯模組是平台獨立的,因此異構系統可以共用相同的庫。

Python不會檢查在兩個環境中的緩衝。首先,Python總是重新編譯,並且不會儲存直接從命令列載入的模組的結果。其次,如果沒有源模組,Python不檢查緩衝。若要支援無源檔案(只有編譯版本)分布,那麼編譯的模組必須放在源檔案目錄中,並且源模組必需不存在。

給專家的建議:

  • 可以在命令列使用-O或者-OO開關來減少編譯模組的大小。-O參數移除assert語句,-OO參數同時移除assert語句和__doc__字串。由於一些程式依賴這些變數,那麼只有當你確認你要這麼做時,才能使用這兩個參數。“最佳化的”模組有一個opt-標籤並且通常更小。未來的髮型版本可能改變最佳化的影響。

  • .pyc檔案中讀取的程式不會比從.py檔案讀取的程式跑得快;.pyc檔案快的地方在於載入。

  • compileall模組可以為目錄中的所有模組建立.pyc檔案。

  • PEP 3147中有關係這點的更多資訊,包括一個決策流程

6.2 Standard Modules

Python提供了標準模組庫,在獨立文檔中描述,名為Python Library Reference(以後叫做Library Reference)。有一些模組內嵌入解譯器中,這些模組不是語言核心的一部分,但是它們是內嵌的,這既是為效能考慮,也提供了訪問如系統調用般的作業系統原生介面的方式。這些模組集合依賴底層平台的配置選項。例如winreg模組只在Windows系統中提供。一個特殊的模組值得注意:sys,這個模組內嵌在所有Python解譯器中。變數sys.ps1sys.ps2定義了主提示符和輔助提示符的字串:

>>> import sys>>> sys.ps1'>>> '>>> sys.ps2'... '>>> sys.ps1 = 'C> 'C> print('Yuck!')Yuck!C>

只有當解譯器以互動模式運行才會定義這兩個變數。

變數sys.path是決定解譯器模組搜尋路徑的字元列表。該變數從環境變數PYTHONPATH,或者內建預設路徑(PYTHONPATH未指定時)初始化。可以使用標準list操作修改它的值:

>>> import sys>>> sys.path.append('/ufs/guido/lib/python')

6.3 The dir() Function

內嵌函數dir()用於搜尋模組定義的名字。返回一個有序字串列表:

>>> import fibo, sys>>> dir(fibo)['__name__', 'fib', 'fib2']>>> dir(sys)  ['__displayhook__', '__doc__', '__excepthook__', '__loader__', '__name__', '__package__', '__stderr__', '__stdin__', '__stdout__', '_clear_type_cache', '_current_frames', '_debugmallocstats', '_getframe', '_home', '_mercurial', '_xoptions', 'abiflags', 'api_version', 'argv', 'base_exec_prefix', 'base_prefix', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 'float_repr_style', 'getcheckinterval', 'getdefaultencoding', 'getdlopenflags', 'getfilesystemencoding', 'getobjects', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettotalrefcount', 'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info', 'intern', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1', 'setcheckinterval', 'setdlopenflags', 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', 'thread_info', 'version', 'version_info', 'warnoptions']

不帶參數使用dir()函數,會列出當前範圍的全部名字:

>>> a = [1, 2, 3, 4, 5]>>> import fibo>>> fib = fibo.fib>>> dir()['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']

需要注意該函數列出所有類型的名字:變數,模組,函數,等等。

dir()不會列出內嵌函數和變數的名字。如果希望列出,這些名字定義在標準模組builtins中:

>>> import builtins>>> dir(builtins)  ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__build_class__', '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']

6.4 Packages

包是使用“圓點模組名”結構化Python模組名字空間的方式。例如,模組名A.B表示包A中的子模組B。就像模組使得不同模組的作者免於擔憂每個模組的全域名字一樣,圓點模組名的使用使得多模組包(如NumPy或者Python映像庫)的作者免於擔憂每個模組的名字。

假設需要為統一音頻檔案和音頻資料的處理設計一個模組的集合(包)。有許多不同的音頻檔案格式(通常通過副檔名辨認,如.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語句首先測試要匯入的項是否在包中存在;如果不存在,Python假設這個項是模組並嘗試載入它。如果最後尋找失敗,ImportError異常拋出。

相對的,使用import item.subitem.subsubitem時,除了最後一項,每一項都必須是包;最後一項可以是模組或者包但是不能是前面的項中定義的類,函數或者變數。

6.4.1 Importing * From a Package

使用from sound.effects import *會發生什嗎?理想情況下,總是期望在檔案系統中找出所有子模組,並全部匯入。全部匯入會耗費很長時間,並且匯入子模組可能會有不期待的副作用,這些副作用應該在顯式匯入時發生。

唯一的解決方案是包作者提供一個包的顯式索引。import語句遵循以下約定:如果包的__init__.py代碼中定義了名為__all__的變數,那麼使用from package import *時會匯入該變數指定的所有模組。當包的新版本發布時,由包作者負責更新列表__all__。如果包作者不希望可以使用from package import *匯入包中的模組,也可以不支援__all__。 例如,檔案sound/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.echoimport sound.effects.surroundfrom sound.effects import *

這個例子中,echosurround模組被匯入到當前名字空間中,因為執行from... import時,它們已經定義在sound.effects包中定義了。(定義了__all__時同樣有效)

儘管使用import *時只有符合特定模式的名字會被匯出,但仍然不建議在生產代碼中使用。

記住,使用form Package import specific_submodle沒有錯誤。實際上,這是推薦的方法,除非當前模組需要使用其他包中的同名模組。

6.4.2 Intra-package References

當包中包含了子包結構(就如例子中的sound包),可以使用絕對匯入的方式引用兄弟包中的子模組。例如,如果模組sound.filters.vocoder需要使用包sound.effects中的echo模組,可以使用from sound.effects import echo

也可以使用from modul import name語句來相對匯入模組。這種方式使用點.指示當前包和相對匯入中涉及的父包。以surround模組為例:

from . import echofrom .. import formatsfrom ..filters import equalizer

相對匯入基於當前模組的名字。由於主模組的名字總是__main__,要當做Python應用主模組使用的模組必須總是使用絕對匯入的方式。

6.4.3 Packages in Multiple Directories

包還支援一個特殊屬性,__path__。在__init__.py中的代碼執行之前,屬性__path__被初始化為一個列表,這個列表包含了持有該__init__.py檔案的目錄的路徑。該變數可修改,這樣做會影響將來的對包內模組和子包的搜尋。

然而這個特性不總是需要的,它可以用來擴充包的模組集合

Footnotes

[1] 實際上,函數定義也是可“執行”的“語句”;模組層級別的函數執行將函數的名字放置到模組的全域符號表中

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.