• 6.1. 更多有关模块的信息
    • 6.1.1. 以脚本的方式执行模块
    • 6.1.2. 模块搜索路径
    • 6.1.3. “编译过的”Python文件

    6.1. 更多有关模块的信息

    模块可以包含可执行的语句以及函数定义。这些语句用于初始化模块。它们仅在模块 第一次 在 import 语句中被导入时才执行。 1 (当文件被当作脚本运行时,它们也会执行。)

    每个模块都有它自己的私有符号表,该表用作模块中定义的所有函数的全局符号表。因此,模块的作者可以在模块内使用全局变量,而不必担心与用户的全局变量发生意外冲突。另一方面,如果你知道自己在做什么,则可以用跟访问模块内的函数的同样标记方法,去访问一个模块的全局变量,modname.itemname

    模块可以导入其它模块。习惯上但不要求把所有 import 语句放在模块(或脚本)的开头。被导入的模块名存放在调入模块的全局符号表中。

    import 语句有一个变体,它可以把名字从一个被调模块内直接导入到现模块的符号表里。例如:

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

    这并不会把被调模块名引入到局部变量表里(因此在这个例子里,fibo 是未被定义的)。

    还有一个变体甚至可以导入模块内定义的所有名称:

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

    这会调入所有非以下划线(_)开头的名称。 在多数情况下,Python程序员都不会使用这个功能,因为它在解释器中引入了一组未知的名称,而它们很可能会覆盖一些你已经定义过的东西。

    注意通常情况下从一个模块或者包内调入 * 的做法是不太被接受的, 因为这通常会导致代码的可读性很差。不过,在交互式编译器中为了节省打字可以这么用。

    如果模块名称之后带有 as,则跟在 as 之后的名称将直接绑定到所导入的模块。

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

    这会和 import fibo 方式一样有效地调入模块, 唯一的区别是它以 fib 的名称存在的。

    It can also be used when utilising from with similar effects:

    1. >>> from fibo import fib as fibonacci
    2. >>> fibonacci(500)
    3. 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模块:

    1. python fibo.py <arguments>

    模块里的代码会被执行,就好像你导入了模块一样,但是 name 被赋值为 "main"。 这意味着通过在你的模块末尾添加这些代码:

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

    你既可以把这个文件当作脚本又可当作一个可调入的模块来使用, 因为那段解析命令行的代码只有在当模块是以“main”文件的方式执行的时候才会运行:

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

    如果模块是被导入的,那些代码是不运行的:

    1. >>> import fibo
    2. >>>

    这经常用于为模块提供一个方便的用户接口,或用于测试(以脚本的方式运行模块从而执行一些测试套件)。

    6.1.2. 模块搜索路径

    当一个名为 spam 的模块被导入的时候,解释器首先寻找具有该名称的内置模块。如果没有找到,然后解释器从 sys.path 变量给出的目录列表里寻找名为 spam.py 的文件。sys.path 初始有这些目录地址:

    • 包含输入脚本的目录(或者未指定文件时的当前目录)。

    • PYTHONPATH (一个包含目录名称的列表,它和shell变量 PATH 有一样的语法)。

    • 取决于安装的默认设置

    注解

    在支持符号链接的文件系统上,包含输入脚本的目录是在追加符号链接后才计算出来的。换句话说,包含符号链接的目录并 没有 被添加到模块的搜索路径上。

    在初始化后,Python程序可以更改 sys.path。包含正在运行脚本的文件目录被放在搜索路径的开头处, 在标准库路径之前。这意味着将加载此目录里的脚本,而不是标准库中的同名模块。 除非有意更换,否则这是错误。更多信息请参阅 标准模块。

    6.1.3. “编译过的”Python文件

    为了加速模块载入,Python在 pycache 目录里缓存了每个模块的编译后版本,名称为 module.version.pyc ,其中名称中的版本字段对编译文件的格式进行编码; 它一般使用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 中有更多细节,包括一个决策流程图。