6. Modules

6. Modules

如果你退出 Python 解释器并重新进入,你做的任何定义(变量和方法)都会丢失。因此,如果你想要编写一些更大的程序,最好使用文本编辑器先编写好,然后运行这个文件。 这就是所谓的创建 脚本随着你的程序变得越来越长,你可能想要将它分成几个文件,这样更易于维护。你还可能想在几个程序中使用你已经编写好的函数,而不用把函数定义拷贝到每个程序中。

为了支持这个功能,Python 有种方法可以把你定义的内容放到一个文件中,然后在脚本或者交互方式中使用。这样的文件叫 模块; 一个模块中的的定义 可以被导入 到其他模块中或者导入到 模块 (the collection of variables that you have access to in a script executed at the top level and in calculator mode).

模块是包含 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, end=' ')
        a, b = b, a+b
    print()

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. More on Modules

模块可以包含可执行语句以及已定义的函数。这些语句通常用于初始化模块。它们只在 第一次 导入时执行。[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

This does not introduce the module name from which the imports are taken in the local symbol table (so in the example, fibo is not defined).

还有种方式可以导入模块中定义的所有名字:

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

This imports all names except those beginning with an underscore (_). 大多数情况下Python程序员不要使用这个便利的方法,因为它会引入一系列未知的名称到解释器中,这很可能隐藏你已经定义的一些东西。

注意通常情况下从其他module或package中导入*是不被赞同的,因为这会降低代码的可读性。不过,在交互式会话中这样用是可以的,它可以让你少敲一些代码。

注意

出于性能考虑,每个模块在每个解释器会话中只导入一遍。因此, 如果你更改了你的模块,你必须重启解释器 – 或者, 如果你想用交互的方式测试你的模块, 可以使用importlib.reload(), e.g.import importlib; importlib.reload(modulename).

6.1.1. Executing modules as scripts

当你用下列的方式运行一个 Python 模块

python fibo.py <arguments>

the code in the module will be executed, just as if you imported it, but with the __name__ set to "__main__"这意味着,通过在你的模块末尾添加此代码︰

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

就可以让此文件既可以作为可执行的脚本,也可以当作可以导入的模块,因为解析命令行的那部分代码只有在模块作为 “main” 文件执行时才被调用:

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

如果模块是被导入的,将不会运行这段代码:

>>>
>>> import fibo
>>>

这种方法通常用来为模块提供一个方便的用户接口,或者用来测试(例如直接运行脚本会执行一组测试用例)。

6.1.2. The Module Search Path

当一个叫spam 的模块被导入, 解释器会先在内置模块中搜索该模块.If not found, it then searches for a file named spam.py in a list of directories given by the variable sys.path.sys.path is initialized from these locations:

  • 脚本所在的目录(如果没有指明文件,则为当前目录)。
  • PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).
  • 与安装相关的默认值。

注意

 在支持符号连接的文件系统中,输入的脚本所在的目录是符号连接指向的目录。 换句话说也就是包含符号链接的目录会被加到目录搜索路径中。

After initialization, Python programs can modify sys.path脚本所在的目录被放置在搜索路径的最开始,也就是在标准库的路径之前。这意味着将会加载当前目录中的脚本,库目录中具有相同名称的模块不会被加载。除非你是有意想替换标准库,否则这应该被当成是一个错误。See section Standard Modules for more information.

6.1.3. “Compiled” Python files

To speed up loading modules, Python caches the compiled version of each module in the __pycache__ directory under the name module.version.pyc, where the version encodes the format of the compiled file; it generally contains the Python version number. For example, in CPython release 3.3 the compiled version of spam.py would be cached as __pycache__/spam.cpython-33.pyc这种命名约定允许由不同发布和不同版本的Python编译的模块同时存在。

Python checks the modification date of the source against the compiled version to see if it’s out of date and needs to be recompiled. 这是完全自动化的过程。同时,编译后的模块是跨平台的,所以同一个库可以在不同架构的系统之间共享。

Python 在两种情况下不检查缓存。First, it always recompiles and does not store the result for the module that’s loaded directly from the command line. 第二,如果没有源模块它不会检查缓存。若要支持没有源文件(只有编译版)的发布,编译后的模块必须在源目录下,并且必须没有源文件的模块。

部分高级技巧:

  • You can use the -O or -OO switches on the Python command to reduce the size of a compiled module. The -O switch removes assert statements, the -OO switch removes both assert statements and __doc__ strings. 因为某些程序可能会依赖于具有这些可用,您应只使用此选项,如果你知道你在做什么。“Optimized” modules have an opt-tag and are usually smaller. Future releases may change the effects of optimization.
  • A program doesn’t run any faster when it is read from a .pyc file than when it is read from a .py file; the only thing that’s faster about .pyc files is the speed with which they are loaded.
  • The module compileall can create .pyc files for all modules in a directory.
  • There is more detail on this process, including a flow chart of the decisions, in PEP 3147.

6.2. Standard Modules

Python 带有一个标准模块库,并发布有单独的文档叫Python 库参考手册(以下简称"库参考手册")。Some modules are built into the interpreter; these provide access to operations that are not part of the core of the language but are nevertheless built in, either for efficiency or to provide access to operating system primitives such as system calls. 这些模块是可配置的,也取决于底层的平台。For example, the winreg module is only provided on Windows systems. One particular module deserves some attention: sys, which is built into every Python interpreter. The variables sys.ps1 and sys.ps2 define the strings used as primary and secondary prompts:

>>>
>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>> sys.ps1 = 'C> '
C> print('Yuck!')
Yuck!
C>

只有在交互式模式中,这两个变量才有定义。

变量sys.path是一个字符串列表,它决定了模块的解释器搜索路径。It is initialized to a default path taken from the environment variable PYTHONPATH, or from a built-in default ifPYTHONPATH is not set. 你可以使用标准的列表操作修改它:

>>>
>>> import sys
>>> sys.path.append('/ufs/guido/lib/python')

6.3. The dir() Function

The built-in function dir() is used to find out which names a module defines. 它返回一个排好序的字符串列表:

>>>
>>> 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']

Without arguments, dir() lists the names you have defined currently:

>>>
>>> a = [1, 2, 3, 4, 5]
>>> import fibo
>>> fib = fibo.fib
>>> dir()
['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']

注意它列出了所有类型的名称: 变量、 模块、 函数等。

dir() does not list the names of built-in functions and variables. If you want a list of those, they are defined in the standard module 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. Packages

包是一种管理 Python 模块命名空间的方式,采用“点分模块名称”。例如,模块名称A.B指定了包A 中名为B 的子模块。就像模块的使用让不同模块的作者不用担心相互间的全局变量名称一样,点分模块的使用让包含多个模块的包(例如 Numpy 和 Python Imaging Library)的作者也不用担心相互之间的模块重名。

假设你想要设计一系列模块(或一个“包”)来统一处理声音文件和声音数据。有很多不同的声音文件格式 (通常承认其扩展名,例如︰ .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)

Note that when using from package import item, the item can be either a submodule (or subpackage) of the package, or some other name defined in the package, like a function, class or variable. The import statement first tests whether the item is defined in the package; if not, it assumes it is a module and attempts to load it. If it fails to find it, an ImportError exception is raised.

Contrarily, when using syntax like import item.subitem.subsubitem, each item except for the last must be a package; the last item can be a module or a package but can’t be a class or function or variable defined in the previous item.

6.4.1. Importing * From a Package

当用户输入 from sound.effects import *时会发生什么?理想情况下,他应该是希望到文件系统中寻找包里面有哪些子模块,并把它们全部导入进来。这可能需要很长时间,而且导入子模块可能会产生想不到的副作用,这些作用本应该只有当子模块是显式导入时才会发生。

唯一的解决办法是包的作者为包提供显式的索引。The import statement uses the following convention: if a package’s __init__.py code defines a list named __all__, it is taken to be the list of module names that should be imported when from package import * is encountered. 当包有新版本包发布时,就需要包的作者更新这个列表了。如果包的作者认为不可以用 import * 方式导入它们的包,也可以决定不支持它。For example, the file sound/effects/__init__.py could contain the following code:

__all__ = ["echo", "surround", "reverse"]

This would mean that from sound.effects import * would import the three named submodules of the sound package.

If __all__ is not defined, the statement from sound.effects import * does not import all submodules from the package sound.effects into the current namespace; it only ensures that the package sound.effects has been imported (possibly running any initialization code in __init__.py) and then imports whatever names are defined in the package. This includes any names defined (and submodules explicitly loaded) by __init__.pyIt also includes any submodules of the package that were explicitly loaded by previous import statements. 请考虑此代码:

import sound.effects.echo
import sound.effects.surround
from sound.effects import *

In this example, the echo and surround modules are imported in the current namespace because they are defined in the sound.effects package when the from...importstatement is executed. (This also works when __all__ is defined.)

Although certain modules are designed to export only names that follow certain patterns when you use import *, it is still considered bad practise in production code.

Remember, there is nothing wrong with using from Package import specific_submodule事实上,这是推荐的写法,除非导入的模块需要使用其它包中的同名子模块。

6.4.2. Intra-package References

When packages are structured into subpackages (as with the sound package in the example), you can use absolute imports to refer to submodules of siblings packages. For example, if the module sound.filters.vocoder needs to use the echo module in the sound.effects package, it can use from sound.effects import echo.

You can also write relative imports, with the from module import name form of import statement. 这些导入使用前导的点号表示相对导入的是从当前包还是上级的包。From thesurround module for example, you might use:

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

注意,相对导入基于当前模块的名称。Since the name of the main module is always "__main__", modules intended for use as the main module of a Python application must always use absolute imports.

6.4.3. Packages in Multiple Directories

Packages support one more special attribute, __path__This is initialized to be a list containing the name of the directory holding the package’s __init__.py before the code in that file is executed. This variable can be modified; doing so affects future searches for modules and subpackages contained in the package.

虽然通常不需要此功能,它可以用于扩展包中的模块的集合。

脚注

[1]In fact function definitions are also ‘statements’ that are ‘executed’; the execution of a module-level function definition enters the function name in the module’s global symbol table.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值