6. 模块 — Python 文档
6. 模块
如果您退出 Python 解释器并再次输入,您所做的定义(函数和变量)将丢失。 因此,如果您想编写一个稍长的程序,最好使用文本编辑器为解释器准备输入,然后使用该文件作为输入来运行它。 这称为创建 脚本 。 随着您的程序变长,您可能希望将其拆分为多个文件以便于维护。 您可能还想使用已在多个程序中编写的方便函数,而无需将其定义复制到每个程序中。
为了支持这一点,Python 有一种方法可以将定义放在文件中,并在脚本或解释器的交互式实例中使用它们。 这样的文件称为模块; 模块中的定义可以 导入 到其他模块或 main 模块(您可以在顶层和计算器模式下执行的脚本中访问的变量集合) .
模块是包含 Python 定义和语句的文件。 文件名是附加后缀 .py
的模块名称。 在模块内,模块的名称(作为字符串)可用作全局变量 __name__
的值。 例如,使用您喜欢的文本编辑器在当前目录中创建一个名为 fibo.py
的文件,其内容如下:
# Fibonacci numbers module
def fib(n): # write Fibonacci series up to n
a, b = 0, 1
while b < n:
print b,
a, b = b, a+b
def fib2(n): # return Fibonacci series up to n
result = []
a, b = 0, 1
while b < n:
result.append(b)
a, b = b, a+b
return 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. 更多关于模块
模块可以包含可执行语句以及函数定义。 这些语句旨在初始化模块。 它们仅在 first 次在 import 语句中遇到模块名称时执行。 1(如果文件作为脚本执行,它们也会运行。)
每个模块都有自己的私有符号表,它被模块中定义的所有函数用作全局符号表。 因此,模块的作者可以在模块中使用全局变量,而不必担心与用户的全局变量发生意外冲突。 另一方面,如果您知道自己在做什么,则可以使用用于引用其功能的相同符号 modname.itemname
来触摸模块的全局变量。
模块可以导入其他模块。 通常但不需要将所有 import 语句放在模块(或脚本,就此而言)的开头。 导入的模块名称放置在导入模块的全局符号表中。
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
这将导入除以下划线开头的所有名称 (_
)。
请注意,通常从模块或包中导入 *
的做法是不受欢迎的,因为它通常会导致代码可读性差。 但是,可以使用它来保存交互式会话中的输入。
如果模块名后面跟着as,那么as后面的名字直接绑定到导入的模块上。
>>> import fibo as fib
>>> fib.fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
这以与 import fibo
相同的方式有效地导入模块,唯一的区别是它作为 fib
可用。
当使用 from 具有类似效果时,也可以使用它:
>>> from fibo import fib as fibonacci
>>> fibonacci(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
笔记
出于效率原因,每个模块在每个解释器会话中仅导入一次。 因此,如果你改变你的模块,你必须重新启动解释器——或者,如果它只是一个你想交互测试的模块,使用 reload(),例如 reload(modulename)
。
6.1.1. 将模块作为脚本执行
当您运行 Python 模块时
python fibo.py <arguments>
模块中的代码将被执行,就像您导入它一样,但将 __name__
设置为 "__main__"
。 这意味着通过在模块末尾添加此代码:
if __name__ == "__main__":
import sys
fib(int(sys.argv[1]))
您可以使文件既可用作脚本又可用作可导入模块,因为解析命令行的代码仅在模块作为“主”文件执行时才运行:
$ python fibo.py 50
1 1 2 3 5 8 13 21 34
如果导入了模块,则不会运行代码:
>>> import fibo
>>>
这通常用于为模块提供方便的用户界面,或用于测试目的(将模块作为脚本执行测试套件运行)。
6.1.2. 模块搜索路径
当导入名为 spam
的模块时,解释器首先搜索具有该名称的内置模块。 如果未找到,它将在变量 sys.path 给出的目录列表中搜索名为 spam.py
的文件。 sys.path 从以下位置初始化:
- 包含输入脚本的目录(或当前目录)。
- PYTHONPATH(目录名称列表,与shell变量
PATH
的语法相同)。 - 依赖于安装的默认值。
初始化后,Python 程序可以修改 sys.path。 包含正在运行的脚本的目录位于搜索路径的开头,在标准库路径之前。 这意味着将加载该目录中的脚本而不是库目录中的同名模块。 这是一个错误,除非更换是有意的。 有关详细信息,请参阅 标准模块 部分。
6.1.3. “已编译”的 Python 文件
作为使用大量标准模块的短程序启动时间的一个重要加速,如果在spam.py
所在的目录中存在一个名为spam.pyc
的文件,则假定这是包含模块 spam
的已经“字节编译”的版本。 用于创建 spam.pyc
的 spam.py
版本的修改时间记录在 spam.pyc
中,如果这些不匹配,则忽略 .pyc
文件。
通常,您无需执行任何操作即可创建 spam.pyc
文件。 每当成功编译 spam.py
时,都会尝试将编译版本写入 spam.pyc
。 如果此尝试失败,则不是错误; 如果由于某种原因文件没有完全写入,则生成的 spam.pyc
文件将被识别为无效,因此稍后会被忽略。 spam.pyc
文件的内容是平台无关的,所以一个Python模块目录可以被不同架构的机器共享。
给专家的一些提示:
当使用 -O 标志调用 Python 解释器时,会生成优化代码并存储在
.pyo
文件中。 优化器目前没有太大帮助; 它只删除 assert 语句。 当使用-O时,优化了all bytecode;.pyc
文件被忽略,.py
文件被编译为优化的字节码。将两个 -O 标志传递给 Python 解释器 (-OO) 将导致字节码编译器执行优化,这在极少数情况下可能会导致程序出现故障。 目前只从字节码中删除了
__doc__
字符串,从而产生更紧凑的.pyo
文件。 由于某些程序可能依赖于提供这些功能,因此只有在您知道自己在做什么时才应使用此选项。从
.pyc
或.pyo
文件中读取程序时,它的运行速度并不比从.py
文件中读取时快;.pyc
或.pyo
文件唯一更快的是它们的加载速度。当脚本通过在命令行上给出其名称来运行时,脚本的字节码永远不会写入
.pyc
或.pyo
文件。 因此,脚本的启动时间可以通过将其大部分代码移动到一个模块并使用一个导入该模块的小型引导脚本来减少。 也可以直接在命令行上命名.pyc
或.pyo
文件。对于同一个模块,可能有一个名为
spam.pyc
(或spam.pyo
当使用 -O)而没有文件spam.py
的文件。 这可用于以中等难度进行逆向工程的形式分发 Python 代码库。模块 compileall 可以为目录中的所有模块创建
.pyc
文件(或.pyo
文件,当使用 -O 时)。
6.2. 标准模块
Python 附带了一个标准模块库,在单独的文档 Python 库参考(以下称为“库参考”)中进行了描述。 一些模块内置于解释器中; 它们提供对不属于语言核心的一部分但仍然内置的操作的访问,无论是为了效率还是提供对操作系统原语(如系统调用)的访问。 这样的模块集是一个配置选项,它也取决于底层平台。 例如,winreg
模块仅在 Windows 系统上提供。 一个特定的模块值得关注:sys,它内置于每个 Python 解释器中。 变量 sys.ps1
和 sys.ps2
定义用作主要和次要提示的字符串:
>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>> sys.ps1 = 'C> '
C> print 'Yuck!'
Yuck!
C>
这两个变量仅在解释器处于交互模式时才定义。
变量 sys.path
是一个字符串列表,用于确定解释器的模块搜索路径。 它被初始化为从环境变量 PYTHONPATH 中获取的默认路径,或者如果 PYTHONPATH 未设置,则从内置默认值中获取. 您可以使用标准列表操作修改它:
>>> import sys
>>> sys.path.append('/ufs/guido/lib/python')
6.3. 这目录() 功能
内置函数 dir() 用于找出模块定义的名称。 它返回一个排序的字符串列表:
>>> import fibo, sys
>>> dir(fibo)
['__name__', 'fib', 'fib2']
>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__name__', '__package__',
'__stderr__', '__stdin__', '__stdout__', '_clear_type_cache',
'_current_frames', '_getframe', '_mercurial', 'api_version', 'argv',
'builtin_module_names', 'byteorder', 'call_tracing', 'callstats',
'copyright', 'displayhook', 'dont_write_bytecode', 'exc_clear', 'exc_info',
'exc_traceback', 'exc_type', 'exc_value', 'excepthook', 'exec_prefix',
'executable', 'exit', 'flags', 'float_info', 'float_repr_style',
'getcheckinterval', 'getdefaultencoding', 'getdlopenflags',
'getfilesystemencoding', 'getobjects', 'getprofile', 'getrecursionlimit',
'getrefcount', 'getsizeof', 'gettotalrefcount', 'gettrace', 'hexversion',
'long_info', 'maxint', 'maxsize', 'maxunicode', 'meta_path', 'modules',
'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1',
'py3kwarning', 'setcheckinterval', 'setdlopenflags', 'setprofile',
'setrecursionlimit', 'settrace', 'stderr', 'stdin', 'stdout', 'subversion',
'version', 'version_info', 'warnoptions']
没有参数, dir() 列出您当前定义的名称:
>>> a = [1, 2, 3, 4, 5]
>>> import fibo
>>> fib = fibo.fib
>>> dir()
['__builtins__', '__name__', '__package__', 'a', 'fib', 'fibo', 'sys']
请注意,它列出了所有类型的名称:变量、模块、函数等。
dir() 没有列出内置函数和变量的名称。 如果你想要一个列表,它们在标准模块 __builtin__ 中定义:
>>> import __builtin__
>>> dir(__builtin__)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException',
'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError',
'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError',
'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning',
'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt',
'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented',
'NotImplementedError', 'OSError', 'OverflowError',
'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError',
'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError',
'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True',
'TypeError', 'UnboundLocalError', 'UnicodeDecodeError',
'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError',
'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning',
'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__',
'__name__', '__package__', 'abs', 'all', 'any', 'apply', 'basestring',
'bin', 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr',
'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright',
'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval',
'execfile', 'exit', 'file', 'filter', 'float', 'format', 'frozenset',
'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input',
'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license',
'list', 'locals', 'long', 'map', 'max', 'memoryview', 'min', 'next',
'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit',
'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round',
'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super',
'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']
6.4. 套餐
包是一种通过使用“带点的模块名称”来构建 Python 模块命名空间的方法。 例如,模块名称 A.B
指定名为 A
的包中名为 B
的子模块。 就像使用模块可以让不同模块的作者不必担心彼此的全局变量名一样,使用带点的模块名称可以让像 NumPy 或 Pillow 这样的多模块包的作者不必担心彼此的模块名称.
假设您要设计一组模块(一个“包”)来统一处理声音文件和声音数据。 有许多不同的声音文件格式(通常通过其扩展名识别,例如:.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
上的目录中搜索包子目录。
需要 __init__.py
文件才能使 Python 将目录视为包含包; 这样做是为了防止具有通用名称的目录(例如 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
时,项目可以是包的子模块(或子包),也可以是包中定义的其他名称,如函数、类或变量。 import
语句首先测试该项目是否在包中定义; 如果不是,它假定它是一个模块并尝试加载它。 如果找不到,则会引发 ImportError
异常。
反之,当使用import item.subitem.subsubitem
这样的语法时,除了最后一项之外,每一项都必须是一个包; 最后一项可以是模块或包,但不能是前一项中定义的类、函数或变量。
6.4.1. 从包中导入 *
现在当用户写 from sound.effects import *
时会发生什么? 理想情况下,人们希望这以某种方式进入文件系统,找到包中存在哪些子模块,并将它们全部导入。 这可能需要很长时间,并且导入子模块可能会产生不需要的副作用,只有在显式导入子模块时才会发生这种情况。
唯一的解决方案是让包作者提供包的显式索引。 import 语句使用以下约定:如果一个包的 __init__.py
代码定义了一个名为 __all__
的列表,那么它被认为是应该导入的模块名称列表遇到 from package import *
。 当软件包的新版本发布时,由软件包作者来保持这个列表是最新的。 包作者也可能决定不支持它,如果他们没有看到从他们的包中导入 * 的用途。 例如,文件 sound/effects/__init__.py
可能包含以下代码:
__all__ = ["echo", "surround", "reverse"]
这意味着 from sound.effects import *
将导入 sound
包的三个命名子模块。
如果 __all__
未定义,语句 from sound.effects import *
会 not 将包 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. 包内引用
子模块经常需要相互引用。 例如,surround
模块可能使用 echo
模块。 事实上,这样的引用非常普遍,以至于 import 语句首先在包含包中查找,然后再在标准模块搜索路径中查找。 因此,surround
模块可以简单地使用 import echo
或 from echo import echofilter
。 如果在当前包(当前模块是其子模块的包)中找不到导入的模块,则 import 语句查找具有给定名称的顶级模块。
当包被构造成子包时(如示例中的 sound
包),您可以使用绝对导入来引用兄弟包的子模块。 例如模块sound.filters.vocoder
需要使用sound.effects
包中的echo
模块,则可以使用from sound.effects import echo
。
从 Python 2.5 开始,除了上述隐式相对导入之外,您还可以使用 from module import name
形式的导入语句编写显式相对导入。 这些显式相对导入使用前导点来指示相关导入中涉及的当前包和父包。 例如,在 surround
模块中,您可以使用:
from . import echo
from .. import formats
from ..filters import equalizer
请注意,显式和隐式相对导入均基于当前模块的名称。 由于主模块的名称始终为 "__main__"
,因此用作 Python 应用程序主模块的模块应始终使用绝对导入。
6.4.3. 多个目录中的包
软件包支持另一种特殊属性,__path__
。 这被初始化为一个列表,其中包含在执行该文件中的代码之前保存包的 __init__.py
的目录的名称。 这个变量是可以修改的; 这样做会影响以后对包中包含的模块和子包的搜索。
虽然此功能并不经常需要,但它可用于扩展包中的模块集。
脚注
- 1
- 事实上,函数定义也是“执行”的“语句”; 模块级函数定义的执行会在模块的全局符号表中输入函数名称。