Python:6. Modules(模块)

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 a < n:
        print(a, end=' ')
        a, b = b, a+b
    print()

def fib2(n):   # return Fibonacci series up to n
    result = []
    a, b = 0, 1
    while a < n:
        result.append(a)
        a, b = b, a+b
    return result

现在进入 Python 解释器并使用以下内容导入此模块 命令:

>>>
import fibo

这不会将 中定义的函数的名称直接添加到 当前命名空间(有关更多详细信息,请参阅 Python 作用域和命名空间); 它只在那里添加模块名称。用 可以访问函数的模块名称:fibofibo

>>>
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. 更多关于模块的信息
模块可以包含可执行语句以及函数定义。 这些语句用于初始化模块。它们仅执行 第一次在导入语句中遇到模块名称时。1(如果文件作为脚本执行,它们也会运行。

每个模块都有自己的私有命名空间,用作全局命名空间 通过模块中定义的所有函数。因此,模块的作者可以 在模块中使用全局变量,无需担心意外冲突 使用用户的全局变量。另一方面,如果你知道你是什么 这样做,您可以使用与用于 请参阅其功能。modname.itemname

模块可以导入其他模块。习惯上但不要求将所有导入语句放在模块(或脚本)的开头 物质)。导入的模块名称(如果放置在模块的顶层) (在任何函数或类之外),添加到模块的全局命名空间中。

导入语句有一个变体,它从 模块直接导入到导入模块的命名空间中。例如:

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

这将导入除以下划线 () 开头的名称之外的所有名称。 在大多数情况下,Python 程序员不会使用此功能,因为它引入了 解释器中的一组未知名称,可能隐藏了一些东西 您已经定义了。_

请注意,通常从模块或包导入的做法是 皱眉,因为它经常导致代码可读性差。但是,可以 使用它来保存交互式会话中的键入内容。*

如果模块名称后跟 ,则名称 以下直接绑定到导入的模块。asas

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

这有效地以与将要执行的方式相同的方式导入模块,唯一的区别是它可用作 .import fibofib

当使用时,也可以使用具有类似效果:

>>>
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 fibo.py
模块中的代码将被执行,就像您导入它一样,但 设置为 。这意味着通过将此代码添加到 模块的结尾:name"main"

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

您可以使文件可用作脚本以及可导入的模块, 因为解析命令行的代码仅在模块 作为“主”文件执行:

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

如果导入模块,则不会运行代码:

>>>
import fibo
>>>

这通常用于为模块提供方便的用户界面,或者 用于测试目的(将模块作为脚本运行将执行测试套件)。

6.1.2. 模块搜索路径
导入名为的模块时,解释器首先搜索 具有该名称的内置模块。这些模块名称在sys.builtin_module_names中列出。如果未找到,则搜索文件 在变量 sys.path 给出的目录列表中命名。sys.path 从以下位置初始化:spamspam.py

包含输入脚本的目录(如果没有,则为当前目录 文件已指定)。

PYTHONPATH(目录名称列表,语法与 壳变量 )。PATH

依赖于安装的默认值(按照约定,包括目录,由站点模块处理)。site-packages

更多详细信息请参阅sys.path模块搜索路径的初始化。

注意 在支持符号链接的文件系统上,包含输入的目录 脚本是在遵循符号链接后计算的。换句话说, 包含符号链接的目录不会添加到模块搜索路径中。
初始化后,Python 程序可以修改 sys.path。这 包含正在运行的脚本的目录位于 搜索路径,位于标准库路径之前。这意味着脚本在那 将加载目录,而不是库中同名的模块 目录。除非打算更换,否则这是一个错误。有关详细信息,请参阅标准模块部分。

6.1.3. “编译”的 Python 文件
为了加快加载模块的速度,Python 缓存了每个模块的编译版本 在名称下的目录中 , 其中版本对编译文件的格式进行编码;它通常包含 Python 版本号。例如,在CPython版本3.3中,编译 spam.py 的版本将缓存为 .这 命名约定允许来自不同版本和不同版本的编译模块 Python版本共存。pycache__module.version.pyc__pycache/spam.cpython-33.pyc

Python 根据编译版本检查源代码的修改日期 以查看它是否已过时并需要重新编译。这是一个完全 自动过程。此外,编译的模块与平台无关,因此 同一库可以在具有不同体系结构的系统之间共享。

Python 在两种情况下不会检查缓存。首先,它总是 重新编译并且不存储直接加载的模块的结果 从命令行。其次,如果没有,它不会检查缓存 源模块。要支持非源代码(仅编译)发行版, 编译后的模块必须在源目录中,并且不能有源 模块。

给专家的一些提示:

您可以在 Python 命令上使用 -O 或 -OO 开关 以减小已编译模块的大小。开关删除断言 语句,开关会同时删除断言语句和__doc__ 字符串。由于某些程序可能依赖于提供这些程序,因此您应该 仅当您知道自己在做什么时才使用此选项。“优化”模块具有 一个标签,通常较小。未来版本可能会 更改优化的效果。-O-OOopt-

从文件中读取程序时运行速度并不比从文件读取程序时快;唯一更快的东西 关于文件是加载它们的速度。.pyc.py.pyc

模块编译都可以为中的所有模块创建 .pyc 文件 目录。

有关此过程的更多详细信息,包括 决定,在PEP 3147中。

6.2. 标准模块
Python 附带了一个标准模块库,在单独的 文档,Python 库参考(以下简称“库参考”)。一些 模块内置于解释器中;这些提供对以下操作的访问: 不是语言核心的一部分,但仍然是内置的,要么 为了提高效率或提供对操作系统基元的访问,例如 系统调用。此类模块的集合是一个配置选项,它也 取决于底层平台。例如,winreg 模块只是 在视窗系统上提供。一个特定的模块值得关注:sys,它内置于每个Python解释器中。变量并定义用作主要和次要的字符串 提示:sys.ps1sys.ps2

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

仅当解释器处于交互模式时,才定义这两个变量。

变量是确定解释器的字符串列表 模块的搜索路径。它初始化为从 环境变量 PYTHONPATH,如果未设置 PYTHONPATH,则来自内置默认值。您可以使用标准列表对其进行修改 操作:sys.path

>>>
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() 不列出内置函数和变量的名称。如果你 想要一个列表,它们在内置的标准模块中定义:

>>>
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 模块命名空间的方法 模块名称”。例如,模块名称指定子模块 在名为 的包中命名。就像使用模块可以节省 不同模块的作者不必担心彼此的全局 变量名,使用虚线模块名省去了多模块的作者 像NumPy或Pillow这样的软件包不必担心 彼此的模块名称。A.BBA

假设您想为制服设计一组模块(“包”) 处理声音文件和声音数据。有许多不同的声音文件 格式(通常通过其扩展名识别,例如:、、),因此您可能需要创建和维护不断增长的 用于在各种文件格式之间转换的模块集合。 您可能还希望对声音数据执行许多不同的操作 (例如混音、添加回声、应用均衡器功能、创建 人工立体效果),所以除此之外,您将编写永无止境的 用于执行这些操作的模块流。这是一个可能的结构 您的软件包(以分层文件系统表示):.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.pystring__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
同样,这会加载子模块,但这使其功能直接可用:echoechofilter()

echofilter(input, output, delay=0.7, atten=4)
请注意,使用 时,该项可以是 包的子模块(或子包),或在 包,如函数、类或变量。声明第一 测试项目是否在包中定义;如果不是,则假定它是一个 模块并尝试加载它。如果找不到它,则会引发 ImportError 异常。from package import itemimport

相反,当使用类似 的语法时,每个项目 除了最后一个必须是包裹;最后一项可以是模块或 包,但不能是上一个中定义的类、函数或变量 项目。import item.subitem.subsubitem

6.4.1. 从软件包导入 *
现在当用户写时会发生什么?理想 人们希望这以某种方式进入文件系统,找到哪个 子模块存在于包中,并将它们全部导入。这可能需要 长时间和导入子模块可能会产生不必要的副作用,应该 仅当显式导入子模块时才会发生。from sound.effects import *

唯一的解决方案是包作者提供 包。import 语句使用以下约定:如果包的代码定义了一个名为 的列表,则将其视为 应在 IS 时导入的模块名称列表 遇到。包作者在以下情况下应使此列表保持最新 发布新版本的包。包作者也可能决定不 支持它,如果他们没有看到从他们的包导入 * 的用途。为 例如,该文件可能包含以下内容 法典:init.py__all__from package import *sound/effects/init.py

all = [“echo”, “surround”, “reverse”]
这意味着将导入三个 包的命名子模块。from sound.effects import *sound.effects

如果未定义,则该语句不会将所有子模块从包导入到 当前命名空间;它只确保包具有 已导入(可能运行 中的任何初始化代码) 然后导入包中定义的任何名称。这包括任何 由 定义的名称(和显式加载的子模块)。它 还包括由 显式加载的包的任何子模块 以前的导入语句。请考虑以下代码:all__from sound.effects import *sound.effectssound.effects__init.py__init__.py

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

在此示例中,和模块导入到 当前命名空间,因为它们是在包中定义的 执行语句时。(这在定义时也有效。echosurroundsound.effectsfrom…import__all__

尽管某些模块旨在仅导出某些模块后面的名称 使用模式时,它仍然被认为是不好的做法 生产代码。import *

请记住,使用 !事实上,这是推荐的符号,除非 导入模块需要使用来自不同的同名子模块 包。from package import specific_submodule

6.4.2. 包内引用
当包被构造成子包时(与包一样 在示例中),您可以使用绝对导入来引用同级的子模块 包。例如,如果模块需要使用 包中的模块,它可以使用.soundsound.filters.vocoderechosound.effectsfrom sound.effects import echo

您还可以编写相对导入,使用表单 的导入语句。这些导入使用前导点来指示当前和 相对导入中涉及的父包。例如,在模块中,您可以使用:from module import namesurround

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

请注意,相对导入基于当前模块的名称。因为 主模块的名称始终为 ,用于使用的模块 作为 Python 应用程序的主模块必须始终使用绝对导入。“main

6.4.3. 多个目录中的软件包
包支持另一个特殊属性,path。这是 初始化为包含保存 包在执行该文件中的代码之前。这 变量可以修改;这样做会影响将来对模块的搜索,并且 包中包含的子包。init.py

虽然通常不需要此功能,但它可用于扩展 在包中找到的模块。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值