Python手冊模組

巴扎黑
發布: 2017-06-23 16:29:36
原創
1418 人瀏覽過

[譯]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=&#39; &#39;)
        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__&#39;fibo&#39;
登入後複製

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

>>> 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&#39;>>> &#39;>>> sys.ps2&#39;... &#39;>>> sys.ps1 = &#39;C> &#39;C> print(&#39;Yuck!&#39;)
Yuck!C>
登入後複製

只有当解释器以交互模式运行才会定义这两个变量。

变量sys.path是决定解释器模块搜索路径的字符列表。该变量从环境变量PYTHONPATH,或者内置默认路径(PYTHONPATH未指定时)初始化。可以使用标准list操作修改它的值:

>>> import sys>>> sys.path.append(&#39;/ufs/guido/lib/python&#39;)
登入後複製

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

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] 实际上,函数定义也是可“执行”的“语句”;模块级别的函数执行将函数的名字放置到模块的全局符号表中

以上是Python手冊模組的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!