6. 模块
如果您退出 Python 解释器并再次输入它,则定义 已经制作(函数和变量)丢失。因此,如果你想写一个 稍长的程序,您最好使用文本编辑器来准备 解释器的输入,并改为使用该文件作为输入运行它。这 称为创建脚本。随着程序变长,您可能需要 将其拆分为多个文件,以便于维护。您可能还想使用 您在多个程序中编写的方便功能,而无需复制其 定义到每个程序中。
为了支持这一点,Python有一种方法可以将定义放在文件中并在 脚本或在解释器的交互式实例中。这样的文件称为模块;模块中的定义可以导入到其他模块或 主模块(您有权在 在顶层和计算器模式下执行的脚本)。
模块是包含 Python 定义和语句的文件。文件名 是附加了后缀的模块名称。在模块中, 模块的名称(作为字符串)可用作全局变量的值。例如,使用您喜欢的文本编辑器创建文件 在当前目录中调用,内容如下:.py__name__fibo.py
# Fibonacci numbers module
def fib(n): # write Fibonacci series up to n
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
print()
def fib2(n): # return Fibonacci series up to n
result = []
a, b = 0, 1
while a < n:
result.append(a)
a, b = b, a+b
return result
现在进入 Python 解释器并使用以下内容导入此模块 命令:
>>>
import fibo
这不会将 中定义的函数的名称直接添加到 当前命名空间(有关更多详细信息,请参阅 Python 作用域和命名空间); 它只在那里添加模块名称。用 可以访问函数的模块名称:fibofibo
>>>
fibo.fib(1000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
fibo.fib2(100)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
fibo.__name__
'fibo'
如果您打算经常使用某个函数,则可以将其分配给本地名称:
>>>
fib = fibo.fib
fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
6.1. 更多关于模块的信息
模块可以包含可执行语句以及函数定义。 这些语句用于初始化模块。它们仅执行 第一次在导入语句中遇到模块名称时。1(如果文件作为脚本执行,它们也会运行。
每个模块都有自己的私有命名空间,用作全局命名空间 通过模块中定义的所有函数。因此,模块的作者可以 在模块中使用全局变量,无需担心意外冲突 使用用户的全局变量。另一方面,如果你知道你是什么 这样做,您可以使用与用于 请参阅其功能。modname.itemname
模块可以导入其他模块。习惯上但不要求将所有导入语句放在模块(或脚本)的开头 物质)。导入的模块名称(如果放置在模块的顶层) (在任何函数或类之外),添加到模块的全局命名空间中。
导入语句有一个变体,它从 模块直接导入到导入模块的命名空间中。例如:
>>>
from fibo import fib, fib2
fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
这不会引入从中导入的模块名称 本地命名空间(因此在示例中未定义)。fibo
甚至还有一个变体可以导入模块定义的所有名称:
>>>
from fibo import *
fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
这将导入除以下划线 () 开头的名称之外的所有名称。 在大多数情况下,Python 程序员不会使用此功能,因为它引入了 解释器中的一组未知名称,可能隐藏了一些东西 您已经定义了。_
请注意,通常从模块或包导入的做法是 皱眉,因为它经常导致代码可读性差。但是,可以 使用它来保存交互式会话中的键入内容。*
如果模块名称后跟 ,则名称 以下直接绑定到导入的模块。asas
>>>
import fibo as fib
fib.fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
这有效地以与将要执行的方式相同的方式导入模块,唯一的区别是它可用作 .import fibofib
当使用时,也可以使用具有类似效果:
>>>
from fibo import fib as fibonacci
fibonacci(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
注意 出于效率原因,每个解释器每个模块仅导入一次 会期。因此,如果更改模块,则必须重新启动 解释器 – 或者,如果它只是您想要交互式测试的一个模块, 使用 importlib.reload(),例如 import importlib; importlib.reload(modulename)。
6.1.1. 将模块作为脚本执行
当您使用
python fibo.py
模块中的代码将被执行,就像您导入它一样,但 设置为 。这意味着通过将此代码添加到 模块的结尾:name"main"
if __name__ == "__main__":
import sys
fib(int(sys.argv[1]))
您可以使文件可用作脚本以及可导入的模块, 因为解析命令行的代码仅在模块 作为“主”文件执行:
python fibo.py 50
0 1 1 2 3 5 8 13 21 34
如果导入模块,则不会运行代码:
>>>
import fibo
>>>
这通常用于为模块提供方便的用户界面,或者 用于测试目的(将模块作为脚本运行将执行测试套件)。
6.1.2. 模块搜索路径
导入名为的模块时,解释器首先搜索 具有该名称的内置模块。这些模块名称在sys.builtin_module_names中列出。如果未找到,则搜索文件 在变量 sys.path 给出的目录列表中命名。sys.path 从以下位置初始化:spamspam.py
包含输入脚本的目录(如果没有,则为当前目录 文件已指定)。
PYTHONPATH(目录名称列表,语法与 壳变量 )。PATH
依赖于安装的默认值(按照约定,包括目录,由站点模块处理)。site-packages
更多详细信息请参阅sys.path模块搜索路径的初始化。
注意 在支持符号链接的文件系统上,包含输入的目录 脚本是在遵循符号链接后计算的。换句话说, 包含符号链接的目录不会添加到模块搜索路径中。
初始化后,Python 程序可以修改 sys.path。这 包含正在运行的脚本的目录位于 搜索路径,位于标准库路径之前。这意味着脚本在那 将加载目录,而不是库中同名的模块 目录。除非打算更换,否则这是一个错误。有关详细信息,请参阅标准模块部分。
6.1.3. “编译”的 Python 文件
为了加快加载模块的速度,Python 缓存了每个模块的编译版本 在名称下的目录中 , 其中版本对编译文件的格式进行编码;它通常包含 Python 版本号。例如,在CPython版本3.3中,编译 spam.py 的版本将缓存为 .这 命名约定允许来自不同版本和不同版本的编译模块 Python版本共存。pycache__module.version.pyc__pycache/spam.cpython-33.pyc
Python 根据编译版本检查源代码的修改日期 以查看它是否已过时并需要重新编译。这是一个完全 自动过程。此外,编译的模块与平台无关,因此 同一库可以在具有不同体系结构的系统之间共享。
Python 在两种情况下不会检查缓存。首先,它总是 重新编译并且不存储直接加载的模块的结果 从命令行。其次,如果没有,它不会检查缓存 源模块。要支持非源代码(仅编译)发行版, 编译后的模块必须在源目录中,并且不能有源 模块。
给专家的一些提示:
您可以在 Python 命令上使用 -O 或 -OO 开关 以减小已编译模块的大小。开关删除断言 语句,开关会同时删除断言语句和__doc__ 字符串。由于某些程序可能依赖于提供这些程序,因此您应该 仅当您知道自己在做什么时才使用此选项。“优化”模块具有 一个标签,通常较小。未来版本可能会 更改优化的效果。-O-OOopt-
从文件中读取程序时运行速度并不比从文件读取程序时快;唯一更快的东西 关于文件是加载它们的速度。.pyc.py.pyc
模块编译都可以为中的所有模块创建 .pyc 文件 目录。
有关此过程的更多详细信息,包括 决定,在PEP 3147中。
6.2. 标准模块
Python 附带了一个标准模块库,在单独的 文档,Python 库参考(以下简称“库参考”)。一些 模块内置于解释器中;这些提供对以下操作的访问: 不是语言核心的一部分,但仍然是内置的,要么 为了提高效率或提供对操作系统基元的访问,例如 系统调用。此类模块的集合是一个配置选项,它也 取决于底层平台。例如,winreg 模块只是 在视窗系统上提供。一个特定的模块值得关注:sys,它内置于每个Python解释器中。变量并定义用作主要和次要的字符串 提示:sys.ps1sys.ps2
>>>
import sys
sys.ps1
'>>> '
sys.ps2
'... '
sys.ps1 = 'C> '
C> print('Yuck!')
Yuck!
C>
仅当解释器处于交互模式时,才定义这两个变量。
变量是确定解释器的字符串列表 模块的搜索路径。它初始化为从 环境变量 PYTHONPATH,如果未设置 PYTHONPATH,则来自内置默认值。您可以使用标准列表对其进行修改 操作:sys.path
>>>
import sys
sys.path.append('/ufs/guido/lib/python')
6.3. dir() 函数
内置函数 dir() 用于找出模块的名称 定义。它返回字符串的排序列表:
>>>
import fibo, sys
dir(fibo)
['__name__', 'fib', 'fib2']
dir(sys)
['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__',
'__interactivehook__', '__loader__', '__name__', '__package__', '__spec__',
'__stderr__', '__stdin__', '__stdout__', '__unraisablehook__',
'_clear_type_cache', '_current_frames', '_debugmallocstats', '_framework',
'_getframe', '_git', '_home', '_xoptions', 'abiflags', 'addaudithook',
'api_version', 'argv', 'audit', 'base_exec_prefix', 'base_prefix',
'breakpointhook', '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', 'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth',
'getallocatedblocks', 'getdefaultencoding', 'getdlopenflags',
'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile',
'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval',
'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info',
'intern', 'is_finalizing', 'last_traceback', 'last_type', 'last_value',
'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks',
'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'pycache_prefix',
'set_asyncgen_hooks', 'set_coroutine_origin_tracking_depth', 'setdlopenflags',
'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr',
'stdin', 'stdout', 'thread_info', 'unraisablehook', 'version', 'version_info',
'warnoptions']
如果没有参数,dir() 会列出您当前定义的名称:
>>>
a = [1, 2, 3, 4, 5]
import fibo
fib = fibo.fib
dir()
['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']
请注意,它列出了所有类型的名称:变量、模块、函数等。
dir() 不列出内置函数和变量的名称。如果你 想要一个列表,它们在内置的标准模块中定义:
>>>
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. 软件包
包是一种通过使用“点分”来构建 Python 模块命名空间的方法 模块名称”。例如,模块名称指定子模块 在名为 的包中命名。就像使用模块可以节省 不同模块的作者不必担心彼此的全局 变量名,使用虚线模块名省去了多模块的作者 像NumPy或Pillow这样的软件包不必担心 彼此的模块名称。A.BBA
假设您想为制服设计一组模块(“包”) 处理声音文件和声音数据。有许多不同的声音文件 格式(通常通过其扩展名识别,例如:、、),因此您可能需要创建和维护不断增长的 用于在各种文件格式之间转换的模块集合。 您可能还希望对声音数据执行许多不同的操作 (例如混音、添加回声、应用均衡器功能、创建 人工立体效果),所以除此之外,您将编写永无止境的 用于执行这些操作的模块流。这是一个可能的结构 您的软件包(以分层文件系统表示):.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.pystring__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
同样,这会加载子模块,但这使其功能直接可用:echoechofilter()
echofilter(input, output, delay=0.7, atten=4)
请注意,使用 时,该项可以是 包的子模块(或子包),或在 包,如函数、类或变量。声明第一 测试项目是否在包中定义;如果不是,则假定它是一个 模块并尝试加载它。如果找不到它,则会引发 ImportError 异常。from package import itemimport
相反,当使用类似 的语法时,每个项目 除了最后一个必须是包裹;最后一项可以是模块或 包,但不能是上一个中定义的类、函数或变量 项目。import item.subitem.subsubitem
6.4.1. 从软件包导入 *
现在当用户写时会发生什么?理想 人们希望这以某种方式进入文件系统,找到哪个 子模块存在于包中,并将它们全部导入。这可能需要 长时间和导入子模块可能会产生不必要的副作用,应该 仅当显式导入子模块时才会发生。from sound.effects import *
唯一的解决方案是包作者提供 包。import 语句使用以下约定:如果包的代码定义了一个名为 的列表,则将其视为 应在 IS 时导入的模块名称列表 遇到。包作者在以下情况下应使此列表保持最新 发布新版本的包。包作者也可能决定不 支持它,如果他们没有看到从他们的包导入 * 的用途。为 例如,该文件可能包含以下内容 法典:init.py__all__from package import *sound/effects/init.py
all = [“echo”, “surround”, “reverse”]
这意味着将导入三个 包的命名子模块。from sound.effects import *sound.effects
如果未定义,则该语句不会将所有子模块从包导入到 当前命名空间;它只确保包具有 已导入(可能运行 中的任何初始化代码) 然后导入包中定义的任何名称。这包括任何 由 定义的名称(和显式加载的子模块)。它 还包括由 显式加载的包的任何子模块 以前的导入语句。请考虑以下代码:all__from sound.effects import *sound.effectssound.effects__init.py__init__.py
import sound.effects.echo
import sound.effects.surround
from sound.effects import *
在此示例中,和模块导入到 当前命名空间,因为它们是在包中定义的 执行语句时。(这在定义时也有效。echosurroundsound.effectsfrom…import__all__
尽管某些模块旨在仅导出某些模块后面的名称 使用模式时,它仍然被认为是不好的做法 生产代码。import *
请记住,使用 !事实上,这是推荐的符号,除非 导入模块需要使用来自不同的同名子模块 包。from package import specific_submodule
6.4.2. 包内引用
当包被构造成子包时(与包一样 在示例中),您可以使用绝对导入来引用同级的子模块 包。例如,如果模块需要使用 包中的模块,它可以使用.soundsound.filters.vocoderechosound.effectsfrom sound.effects import echo
您还可以编写相对导入,使用表单 的导入语句。这些导入使用前导点来指示当前和 相对导入中涉及的父包。例如,在模块中,您可以使用:from module import namesurround
from . import echo
from .. import formats
from ..filters import equalizer
请注意,相对导入基于当前模块的名称。因为 主模块的名称始终为 ,用于使用的模块 作为 Python 应用程序的主模块必须始终使用绝对导入。“main”
6.4.3. 多个目录中的软件包
包支持另一个特殊属性,path。这是 初始化为包含保存 包在执行该文件中的代码之前。这 变量可以修改;这样做会影响将来对模块的搜索,并且 包中包含的子包。init.py
虽然通常不需要此功能,但它可用于扩展 在包中找到的模块。