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 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. 有关模块的更多信息
=======================

一个模块可以包括可执行的声明以及函数定义。这些声明是用来初始化模块的。
它们只有在当模块名第一次出现在调入名令中时会被执行。（当文件被当作脚本
运行时，它们也会执行。）

每个模块都有它自己的私有符号表，该表被定义在该模块里的所有函数当作全局
符号表使用。因此，一个模块的作者可以在模块内放心使用全局变量，而不必担
心它们会和模块使用者的全局变量发生意外冲突。另一方面，如果您明白接下来
这么做的影响，您可以用跟访问模块内的函数的同样标记方法，去访问一个模块
的全局变量，"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

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

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

如果模块名后跟有 "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"
的名称存在的。

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

   >>> 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
   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文件
---------------------------

为了加速模块载入，Python在 "__pycache__" 目录里缓存了每个模块的编译后
版本，名称为 "module.*version*.pyc"。 名称中的版本字段编码了编译文件的
格式， 它一般使用Python版本号。例如，在CPython版本3.3中，spam.py的编译
版本将被缓存为 "__pycache__/spam.cpython-33.pyc"。此命名约定允许来自不
同版本的Python的已编译模块共存。

Python核查源文件的更改日期相较于编译后的版本是否过期和需要重新编译。这
是一个完全自动化的过程。同时，编译后的模块是不依赖于系统的，因此同一个
库可以在不同架构的系统间共享。

Python在两种情形下不会核验缓存。第一，对于从命令行直接载入的模块，它从
来都是重新编译并且不存储编译结果；第二，如果没有源模块，它不会核验缓存
。为了支持无源文件发行版本（只含编译过的）， 编译模块必须是在源目录下
，并且绝对不能有源模块。

给专业人士的一些小建议:

* 你可以在Python命令中使用 "-O" 或者 "-OO" 开关， 以减小编译后模块的
  大 小。 "-O" 开关去除断言声明，"-OO" 开关同时去除断言声明和文档字符
  串。 由于有些程序或许依赖于这些声明和文档，您应当只有在清楚这么做的
  后果时 才使用这个选项。“优化过的”模块有一个 "opt-" 标签并且通常小些
  。将来的 发行版本或许会更改优化的效果。

* 一个从 ".pyc" 文件读出的程序并不会比它从 ".py" 读出时运行的更快，
  ".pyc" 文件唯一快的地方在于载入速度。

* "compileall" 模块可以为一个目录下的所有模块创建.pyc文件。

* 关于这个过程，**PEP 3147** 中有更多细节，包括一个决策流程图。


6.2. 标准模块
=============

Python自带一个标准模块的库，它在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)  
   ['__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']

没有参数时，"dir()" 列出您当前定义过的名称:

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

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

"dir()" 不会列出内置的函数名和变量名。 如果您想要这些，它们的定义是在
标准模块:mod:*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模块的方式。 例如，模块名：
mod:*A.B* 指代“A”包中名为“B”的子模块。正如模块的使用可以免除不同模块开
发者担忧各自的宏变量名，使用加点的模块名，可以免除多模块的包如Numpy或
Pillowd开发者，担忧各自的模块名。

假设你想为声音文件和声音数据的统一处理，设计一个模块集合（一个“包”）。
由于存在很多不同的声音文件格式（通常由它们的扩展名来识别，例如：
".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__.py" 文件。这是为
了避免一个含有烂俗名字的目录无意中隐藏了稍后在模块搜索路径中出现的有效
模块，比如 "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" 这样的语法时，除了last之
外的每个项都必须是一个包；最后一项可以是模块或包，但不能是类或函数或变
量。


6.4.1. 使用 * 导入
------------------

Now what happens when the user writes "from sound.effects import *"?
Ideally, one would hope that this somehow goes out to the filesystem,
finds which submodules are present in the package, and imports them
all.  This could take a long time and importing sub-modules might have
unwanted side-effects that should only happen when the sub-module is
explicitly imported.

The only solution is for the package author to provide an explicit
index of the package.  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.  It is up to the
package author to keep this list up-to-date when a new version of the
package is released.  Package authors may also decide not to support
it, if they don't see a use for importing * from their package.  For
example, the file "sound/effects/__init__.py" could contain the
following code:

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

这意味着 "from sound.effects import *" 将导入 "sound" 包的三个命名子模
块。

如果没有定义 "__all__"，"from sound.effects import *" 语句*不*会从包
"sound.effects" 中导入所有子模块到当前命名空间；它只确保导入了包
"sound.effects" （可能运行任何在 "__init__.py" 中的初始化代码），然后
导入包中定义的任何名称。

   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" 。

您还可以使用import语句的 "from module import name" 形式编写相对导入。
这些导入使用前导点来指示相对导入中涉及的当前和父包。例如，从
"surround" 模块，您可以使用:

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

请注意，相对导入是基于当前模块的名称进行导入的。由于主模块的名称总是
""__main__"" ，因此用作Python应用程序主模块的模块必须始终使用绝对导入
。


6.4.3. 多个目录中的包
---------------------

包支持另一个特殊属性 "__path__" 。这被初始化为一个列表，其中包含在执行
该文件中的代码之前保存包的文件 "__init__.py" 的目录的名称。这个变量可
以修改；做会影响搜索包中包含的模块和子包。

虽然通常不需要此功能，但它可用于扩展程序包中的模块集。

-[ 脚注 ]-

[1] 实际上，函数定义也是‘声明’，并会运行；一个模块函数定义的运行会
    进入 到此模块全局符号表里的此函数名。
