6. 模块

退出 Python 解释器再次进入之前 Python 解释器定义函数变量丢失因此编写程序最好文本编辑器代替解释器执行文件中的输入内容就是编写 脚本随着程序越来越为了方便维护最好脚本分成多个文件编写脚本好处不同程序调用同一函数不用函数定义复制各个程序

实现这些需求,Python 各种定义存入文件脚本解释器交互实例使用这个文件就是 模块模块中的定义可以 导入 其他模块 模块顶层计算器模式执行脚本访问变量)。

模块包含 Python 定义语句文件文件模块后缀 .py 。模块内部通过全局变量 __name__ 可以获取模块字符串)。例如文本编辑器当前目录创建 fibo.py 文件输入以下内容

#
斐波那契数列模块

def fib(n): #
打印斐波那契数列直到 n
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
print()

def fib2(n): #
返回斐波那契数列直到 n
result = []
a, b = 0, 1
while a < n:
result.append(a)
a, b = b, a+b
return result

现在进入 Python 解释器以下命令导入模块
>>>

import fibo

操作不会直接 fibo 定义函数名称添加当前 namespace 参阅 Python 作用命名空间 了解详情);只是模块名称 fibo 添加那里使用模块名称可以访问其中函数:
>>>

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.
模块详解

模块包含可执行语句函数定义这些语句用于初始化模块 import 语句 第一次 遇到模块执行。[1] (文件作为脚本运行时执行这些语句。)

模块自己私有命名空间用作模块定义所有函数全局命名空间因此模块作者可以模块使用全局变量不必担心用户全局变量发生意外冲突另一方面如果知道怎么可以通过引用模块函数一样标记 modname.itemname 访问模块全局变量

模块可以导入其他模块根据惯例可以所有 import 语句模块或者可以脚本开头并非强制要求如果放置模块最高层级导入模块名称添加模块全局命名空间

还有一种 import 语句变化形式可以来自模块名称直接导入导入模块命名空间例如:
>>>

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

这种方式导入所有以下划线(_)开头名称大多数情况不要这个功能这种方式解释器导入未知名称可能覆盖已经定义名称

注意一般情况建议模块导入 *,因为操作经常代码变得难以理解不过为了交互会话几个这么没问题

模块使用 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

备注

为了保证运行效率每次解释器会话导入一次模块如果更改模块内容必须重启解释器交互测试模块可以使用 importlib.reload(),例如 import importlib; importlib.reload(modulename)。
6.1.1.
脚本方式执行模块

可以以下方式运行 Python 模块

python fibo.py <arguments>

操作执行模块代码导入模块一样 __name__ 赋值 "__main__"。 也就是下列代码添加模块末尾

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

这个文件用作脚本用作导入模块因为解析命令行参数代码只有模块作为“main”文件执行运行

python fibo.py 50
0 1 1 2 3 5 8 13 21 34

这个模块导入其它模块代码运行
>>>

import fibo


常用模块提供便捷用户接口用于测试模块作为执行测试套件脚本运行)。
6.1.2.
模块搜索路径

导入名为 spam 模块解释器首先搜索具有名称内置模块这些模块名称 sys.builtin_module_names 列出如果找到变量 sys.path 目录列表搜索名为 spam.py 文件。 sys.path 这些位置初始化:

命令行直接运行脚本所在目录指定文件当前目录)。

PYTHONPATH (
目录列表 shell 变量 PATH 语法一样)。

依赖安装默认按照惯例包括 site-packages 目录 site 模块处理)。

细节参阅 sys.path 模块搜索路径初始化

备注

支持符号链接文件系统,“命令行直接运行脚本所在目录符号链接最终指向目录换句话说符号链接所在目录 没有 添加模块搜索路径

初始化,Python 程序可以更改 sys.path。脚本所在目录标准所在路径搜索意味着脚本所在目录如果标准同名文件那么加载目录不是标准一般错误除非这样替换有意详见 标准模块
6.1.3. “
编译” Python 文件

为了快速加载模块,Python 模块编译版本存在 __pycache__ 目录文件名为 module.version.pyc,version 编译文件格式进行编码一般 Python 版本例如,CPython 3.3 发行,spam.py 编译版本缓存 __pycache__/spam.cpython-33.pyc。这种命名惯例不同 Python 版本编译模块可以共存

Python
对比编译源码修改日期查看编译是否过期是否重新编译进程完全自动此外编译模块平台无关因此不同架构系统之间共享相同

Python
情况检查缓存从命直接载入模块每次都会重新编译储存编译结果没有模块不会检查缓存为了隐藏源代码形式分发通过所有源代码变为编译版本),编译模块必须目录缓存目录并且目录不能包含同名编译模块

专业人士一些建议

Python 命令使用 -O -OO 开关可以减小编译模块大小。-O 去除断言语句,-OO 去除断言语句 __doc__ 字符串有些程序可能依赖这些内容因此没有十足把握不要使用选项。“优化模块带有 opt- 标签并且文件通常将来发行或许改进优化效果

.pyc 文件读取程序不比 .py 读取执行速度,.pyc 文件只是加载速度

compileall
模块可以目录所有模块创建 .pyc 文件

过程细节决策流程图详见 PEP 3147。

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()
函数

内置函数 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()
不会列出内置函数变量名称这些内容定义标准模块 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.


通过使用点号模块构造 Python 模块命名空间一种方式例如模块 A.B 表示名为 A 名为 B 模块使用模块可以不同模块作者不必担心彼此全局变量一样使用点号模块可以 NumPy Pillow 多模块作者不必担心彼此模块冲突

假设统一处理声音文件声音数据设计模块(“”)。声音文件格式通常扩展名识别例如:.wav,.aiff,.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/
用于过滤器
__init__.py
equalizer.py
vocoder.py
karaoke.py
...

导入,Python 搜索 sys.path 目录查找子目录

需要 __init__.py 文件才能 Python 包含文件目录当作处理除非使用 namespace package,相对高级特性)。 可以防止重名目录 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)

Import
语句一种变体直接导入所需函数变量

from sound.effects.echo import echofilter

同样加载模块 echo,使函数 echofilter() 直接:

echofilter(input, output, delay=0.7, atten=4)

注意使用 from package import item ,item 可以模块),可以定义函数变量其他名称。import 语句首先测试是否定义 item;如果定义假定 item 模块尝试加载如果找不到 item,触发 ImportError 异常

相反使用 import item.subitem.subsubitem 句法最后 item 必须最后可以模块不能定义函数变量
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.effects 命名模块

注意模块可能受到本地定义名称影响例如如果 sound/effects/__init__.py 文件添加 reverse 函数,from sound.effects import * 导入 echo surround 个子模块 不会 导入 reverse 模块因为本地定义 reverse 函数遮挡:

__all__ = [
"echo", #
指向 'echo.py' 文件
"surround", #
指向 'surround.py' 文件
"reverse", # !!!
现在指向 'reverse' 函数 !!!
]

def reverse(msg: str): # <--
名称覆盖 'reverse.py' 模块
return msg[::-1] #
针对 'from sound.effects import *' 情况

如果没有定义 __all__,from sound.effects import * 语句 不会 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.
相对导入

个子构成示例中的 sound 可以使用绝对导入引用同级模块例如如果 sound.filters.vocoder 模块需要使用 sound.effects 中的 echo 模块可以使用 from sound.effects import echo。

可以编写相对导入代码使用 from module import name 形式 import 语句这些导入使用前导点号表示相对导入涉及当前上级例如对于 surround 模块可以使用:

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

注意相对导入基于当前模块因为模块永远 "__main__" ,所以如果计划模块用作 Python 应用程序模块那么模块导入语句必须始终使用绝对导入
6.4.3.
目录中的

支持特殊属性, __path__ 。 执行文件中的代码之前初始化字符串 sequence,其中包含 __init__.py 目录名称这个变量可以修改修改影响今后模块包含搜索

这个功能虽然常用用于扩展中的模块

备注
[1]

实际上函数定义执行语句模块函数定义执行函数名称添加模块全局命名空间