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程序员不推荐使用这个便利的方法,因为它会引入一系列未知的名称到解释器中,这很可能覆盖你已经定义的一些东西。

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

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

6.1.1. 以脚本方式执行模块

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

python fibo.py

模块中的代码将会被执行,就像导入它一样,不过此时name被设置为”_main_“。这意味着,如果在你的模块末尾添加此代码:

if name == “main“:
import sys
fib(int(sys.argv[1]))

就可以让此文件像作为模块导入时一样作为脚本执行。因为命令行的代码只有在模块作为”main”文件执行时才会运行:
(这里初看时没理解,现在想明白了,应该是初看时没理解模块和脚本的区别。这里的模块是指用来导入给其他程序的,而脚本就是自己运行的)

在命令行输入 python fibo.py 50
会得到 1 1 2 3 4 8 13 21 34
这样就相当于把它当做脚本使用了。
而如果把它当做模块使用时,将不会发生任何事,if _name_…后的程序没有被运行:

import fibo

这种方法通常用来为模块提供一个方便的用户接口,或者用来测试模块。

6.1.2.模块搜索路径

当一个叫spam的模块被导入,解释器会现在内置模块中搜索该模块。如果没有找到,它会接着到sys.path变量给出的目录中查找名为spam.py的文件。sys.path变量的初始值来自这些位置:
- 脚本所在的目录(如果没有指明文件,则为当前目录)。
- PYTHONPATH(一个包含目录名的列表,与shell变量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 字符串。

  • 因为某些程序依赖于这些变量的可用性,你应该只在确定无误的场合使用这一选项。“优化的” 模块有一个 .pyo 后缀而不是 .pyc 后缀。未来的版本可能会改变优化的效果。

  • 来自 .pyc 文件或 .pyo 文件中的程序不会比来自 .py 文件的运行更快;.pyc 或 .pyo 文件只是在它们加载的时候更快一些。

  • compileall 模块可以为指定目录中的所有模块创建 .pyc 文件(或者使用 -O 参数创建 .pyo 文件)。

  • 在 PEP 3147 中有很多关这一部分内容的细节,并且包含了一个决策流程。

6.2标准模块

Python 带有一个标准模块库,并发布有独立的文档,名为 Python 库参考手册(此后称其为“库参考手册”)。有一些模块内置于解释器之中,这些操作的访问接口不是语言内核的一部分,但是已经内置于解释器了。这既是为了提高效率,也是为了给系统调用等操作系统原生访问提供接口。这类模块集合是一个依赖于底层平台的配置选项。例如,winreg 模块只提供在 Windows 系统上才有。有一个具体的模块值得注意:sys,这个模块内置于所有的 Python 解释器。变量 sys.ps1 和 sys.ps2 定义了主提示符和辅助提示符字符串:

import sys
sys.ps1
'In : '
sys.ps2
'...: '
sys.ps1 = 'C> '
sys.ps1
'C> '

这两个变量只在解释器的交互模式下有意义。

变量sys.path是解释器模块搜索路径的字符串列表。 它由环境变量PYTHONPATH初始化,如果没有设定PYTHONPATH,就由内置的默认值初始化。你可以用标准的字符串操作修改它:

import sys
#sys.path.append()

6.3 dir()函数

内置函数dir()用于按模块名搜索模块定义,它返回一个字符串类型的存储列表:

import fibo, sys
print(dir(fibo))
print()
print(dir(sys))
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'fib', 'fib2']

['__displayhook__', '__doc__', '__excepthook__', '__interactivehook__', '__loader__', '__name__', '__package__', '__spec__', '__stderr__', '__stdin__', '__stdout__', '_clear_type_cache', '_current_frames', '_debugmallocstats', '_getframe', '_git', '_home', '_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', 'get_asyncgen_hooks', 'get_coroutine_wrapper', 'getallocatedblocks', 'getcheckinterval', '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', 'ps3', 'set_asyncgen_hooks', 'set_coroutine_wrapper', '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()
['In',
 'Out',
 '_',
 '_1',
 '_2',
 '_5',
 '_9',
 '__',
 '___',
 '__builtin__',
 '__builtins__',
 '__doc__',
 '__loader__',
 '__name__',
 '__package__',
 '__spec__',
 '_dh',
 '_i',
 '_i1',
 '_i10',
 '_i11',
 '_i12',
 '_i2',
 '_i3',
 '_i4',
 '_i5',
 '_i6',
 '_i7',
 '_i8',
 '_i9',
 '_ih',
 '_ii',
 '_iii',
 '_oh',
 'a',
 'exit',
 'fib',
 'fibo',
 'get_ipython',
 'quit',
 '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',
 'ModuleNotFoundError',
 'NameError',
 'None',
 'NotADirectoryError',
 'NotImplemented',
 'NotImplementedError',
 'OSError',
 'OverflowError',
 'PendingDeprecationWarning',
 'PermissionError',
 'ProcessLookupError',
 'RecursionError',
 'ReferenceError',
 'ResourceWarning',
 'RuntimeError',
 'RuntimeWarning',
 'StopAsyncIteration',
 'StopIteration',
 'SyntaxError',
 'SyntaxWarning',
 'SystemError',
 'SystemExit',
 'TabError',
 'TimeoutError',
 'True',
 'TypeError',
 'UnboundLocalError',
 'UnicodeDecodeError',
 'UnicodeEncodeError',
 'UnicodeError',
 'UnicodeTranslateError',
 'UnicodeWarning',
 'UserWarning',
 'ValueError',
 'Warning',
 'ZeroDivisionError',
 '__IPYTHON__',
 '__build_class__',
 '__debug__',
 '__doc__',
 '__import__',
 '__loader__',
 '__name__',
 '__package__',
 '__spec__',
 'abs',
 'all',
 'any',
 'ascii',
 'bin',
 'bool',
 'bytearray',
 'bytes',
 'callable',
 'chr',
 'classmethod',
 'compile',
 'complex',
 'copyright',
 'credits',
 'delattr',
 'dict',
 'dir',
 'display',
 'divmod',
 'enumerate',
 'eval',
 'exec',
 'filter',
 'float',
 'format',
 'frozenset',
 'get_ipython',
 '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',
 'range',
 'repr',
 'reversed',
 'round',
 'set',
 'setattr',
 'slice',
 'sorted',
 'staticmethod',
 'str',
 'sum',
 'super',
 'tuple',
 'type',
 'vars',
 'zip']

6.4 包

包通常是使用用“圆点模块名”的结构化模块命名空间。例如,名为 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 搜索路径查找包含这个包的子目录。

为了让 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 方式导入包时,这个子项(item)既可以是包中的一个子模块(或一个子包),也可以是包中定义的其它命名,像函数、类或变量。import 语句首先核对是否包中有这个子项,如果没有,它假定这是一个模块,并尝试加载它。如果没有找到它,会引发一个 ImportError 异常。

相反,使用类似 import item.subitem.subsubitem 这样的语法时,这些子项必须是包,最后的子项可以是包或模块,但不能是前面子项中定义的类、函数或变量。

6.4.1.从*导入包

import * 这样的语句理论上是希望文件系统找出包中所有的子模块,然后导入它们。这可能会花长时间,并出现边界效应等。Python 解决方案是提供一个明确的包索引。

这个索引由 __init__.py 定义 __all__ 变量,该变量为一列表,如上例 sound/effects 下的 __init__.py 中,可定义
__all__ = ["echo","surround","reverse"]

这意味着,from sound.effects import * 会从对应的包中导入以上三个子模块; 尽管提供 import *的方法,仍不建议在生产代码中使用这种写法。

6.4.2.包内引用

如果是子包内的引用,可以按相对位置引入子模块 以 echo 模块为例,可以引用如下:

from . import reverse #同级目录 导入reverse
from .. import frormats #上级目录 导入 frormats
from ..filters import equalizer    #上级目录的filters模块下 导入 equalizer

6.4.3 多重目录中的包

包支持一个更为特殊的特性, __path__。在包的__init__.py文件代码执行前,该变量初始化一个目录名列表。作用于子包和模块的搜索功能。

该功能可以用于扩展包中的模块集,不过不常用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值