模块
如果从Python解释器退出并再次输入,则所做的定义(函数和变量)将丢失。因此,如果要编写更长的程序,最好使用文本编辑器为解释器准备输入,然后使用该文件作为输入运行它。这称为创建脚本。随着程序时间的延长,你可能需要将其拆分为多个文件,以便于维护。你可能还想使用在多个程序中编写的便捷功能,而无需将其定义复制到每个程序中。
为此,Python提供了一种将定义放入文件中并在脚本或解释器的交互式实例中使用它们的方法。这样的文件称为模块;可以将模块中的定义导入其他模块或主模块中。
模块是包含Python定义和语句的文件。文件名是模块名称,后缀.py。在模块内,模块名称(作为字符串)可以用作全局变量__name__的值。例如,使用喜欢的文本编辑器在当前目录中创建一个名为fibo.py的文件,其内容如下:
import Fibo # 导入Fibo模块
Fibo.fib(1000) # 调用fib函数
print(Fibo.fib2(1000)) # 调用fib2函数
new_fibo = Fibo.fib # 使用本地名称调用函数
new_fibo(1000) # 与调用fib结果一样
更多关于模块的内容
模块可以包含可执行语句以及函数定义。这些语句旨在初始化模块。仅在import语句中第一次遇到模块名称时才执行它们。(如果文件作为脚本执行,它们也会运行。)
每个模块都有自己的专用符号表,模块中定义的所有功能都将其用作全局符号表。因此,模块的作者可以在模块中使用全局变量,而不必担心与用户的全局变量的意外冲突。
另一方面,如果你知道自己在做什么,则可以使用与引用其功能相同的符号来触摸模块的全局变量modname.itemname。
模块可以导入其他模块。通常,但不需要将所有导入语句放在模块(或脚本)的开头。导入的模块名称放置在导入模块的全局符号表中。
import语句有一个变体,可以将名称从模块直接导入到导入模块的符号表中。
from fibo import fib, fib2
fib(500)
这不会在本地符号表中引入用于导入的模块名称(因此,在示例中未定义fibo)。
甚至还有一个变体来导入模块定义的所有名称:
from fibo import *
fib(500)
这将导入除以下划线(_)开头的名称以外的所有名称。在大多数情况下,Python程序员不使用此功能,因为它在解释器中引入了一组未知的名称,可能会隐藏你已经定义的某些内容。
请注意,通常不赞成从模块或包中导入*,因为这通常会导致代码可读性差。但是,可以使用它来保存交互式会话中的键入。
如果模块名称后跟as,则名称后跟as直接绑定到导入的模块。
import fibo as fib
fib.fib(500)
这与导入fibo的方式一样,实际上是在导入模块,唯一的区别是可以作为fib获得。
这种形式也可以使用在from的形式中:
from fibo import fib as fibonacci
fibonacci(500)
出于效率原因,每个模块在每个解释器会话中仅导入一次。因此,如果您更改模块,则必须重新启动解释器;或者,如果您只是要交互式测试的一个模块,请使用importlib.reload(),例如导入importlib; importlib.reload(模块名称)。
将模块作为脚本执行
该模块中的代码将被执行,就像您导入它一样,但是__name__设置为“ __main__”。这意味着通过在模块末尾添加以下代码:
if __name__ == "__main__":
import sys
fib(int(sys.argv[1]))
你可以使该文件用作脚本以及可导入模块,因为解析命令行的代码仅在将模块作为“main”文件执行时才运行:
$ python fibo.py 50
$ 0 1 1 2 3 5 8 13 21 34
如果模块已导入,则代码不会运行:
import fibo
# 代码不会执行
这通常用于为模块提供方便的用户接口,或用于测试目的(在脚本执行测试套件时运行模块)。
模块搜索路径
导入名为spam的模块时,解释器首先搜索具有该名称的内置模块。如果找不到,它将在变量sys.path给出的目录列表中搜索名为spam.py的文件。 sys.path从以下位置初始化:
- 包含输入脚本的目录(如果未指定文件,则为当前目录)。
- PYTHONPATH(目录名称列表,与hell变量PATH具有相同的语法)。
- 取决于安装的默认值。
在支持符号链接的文件系统上,在遵循符号链接之后计算包含输入脚本的目录。换句话说,包含符号链接的目录不会添加到模块搜索路径中。
初始化后,Python程序可以修改sys.path。包含正在运行的脚本的目录位于搜索路径的开始,在标准库路径之前。这意味着将加载该目录中的脚本,而不是库目录中相同名称的模块。除非打算进行更换,否则这是一个错误。
变量sys.path是一个字符串列表,用于确定解释器的模块搜索路径。它初始化为从环境变量PYTHONPATH提取的默认路径,或者如果未设置PYTHONPATH则从内置默认值初始化。你可以使用标准列表操作对其进行修改:
import sys
sys.path.append('/ufs/guido/lib/python')
dir()函数
内置函数dir()用于找出模块定义的名称。它返回一个排序的字符串列表:
a = [1, 2, 3, 4, 5]
import fibo
fib = fibo.fib
dir()
# ['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']
请注意,它列出了所有类型的名称:变量,模块,函数等。
不带参数的dir()列出您当前定义的名称:
print(dir())
# ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'fib', 'fib2']
dir()不列出内置函数和变量的名称。如果需要这些列表,则在内置的标准模块中定义它们:
import builtins
dir(builtins)
output:
['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']
包
包是通过使用“点分模块名称”来构造Python模块名称空间的一种方式。例如,模块名称A.B在名为A的包中指定了一个名为B的子模块。就像使用模块可以节省不同模块的作者不必担心彼此的全局变量名称一样,使用带点划线的模块名称也可以让作者对于多模块软件包(例如NumPy或Pillow)不必担心彼此的模块名称。
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将包含该文件的目录视为程序包。这样可以防止使用通用名称(例如字符串)的目录无意间隐藏了以后在模块搜索路径中出现的有效模块。在最简单的情况下,__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 import项时,该项可以是包的子模块(或子包),也可以是包中定义的其他名称,例如函数,类或变量。 import语句首先测试项目是否在包装中定义;如果不是,则假定它是一个模块并尝试加载它。如果找不到它,则会引发ImportError异常。
相反,当使用诸如import item.subitem.subsubitem之类的语法时,除最后一项外,其他所有项都必须是一个包。最后一项可以是模块或包,但不能是上一项中定义的类或函数或变量。
从一个包导入*
现在,当用户from sound.effects import *书写时会发生什么?理想情况下,希望这种方式能够进入文件系统,找到包中存在哪些子模块,然后将其全部导入。这可能会花费很长时间,并且导入子模块可能会产生有害的副作用,这些副作用只有在明确导入子模块时才会发生。
唯一的解决方案是让包作者提供程序包的显式索引。 import语句使用以下约定:如果软件包的__init__.py代码定义了名为__all__的列表,则将其视为遇到from package import*时应导入的模块名称的列表。发行新版本的软件包时,软件包作者有责任使此列表保持最新。如果软件包作者没有看到从软件包中导入*的用途,他们可能还会决定不支持它。例如,文件sound/effects/__ init__.py可能包含以下代码:
__all__ = ["echo", "surround", "reverse"]
这意味着from sound.effects import *将导入sound包的三个命名子模块。
如果未定义__all__,则from sound.effects import *的语句不会将包sound.effects中的所有子模块导入当前名称空间;它仅确保已导入包sound.effects(可能在__init__.py中运行任何初始化代码),然后导入包中定义的任何名称。这包括__init__.py定义的任何名称(以及明确加载的子模块)。它还包括以前的import语句显式加载的包的所有子模块。考虑以下代码:
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没有任何问题!实际上,这是推荐的表示法,除非导入模块需要使用来自不同软件包的具有相同名称的子模块。
from sound.effects import effect
effect.effec()
from sound.filters import filter
filter.filte()
from sound.formats import format
format.forma()
from sound.formats.format import forma
forma()
from sound.effects.effect import effec
effec()
from sound.filters.filter import filte
filte()
import sound.effects.effect
sound.effects.effect.effec()
import sound.filters.filter
sound.filters.filter.filte()
import sound.formats.format
sound.formats.format.forma()
from sound.effects import *
effect.effec()
effect2.effec2()
effect3.effec3()
结构化包引用
将包结构化为子包时(如示例中的sounds包一样),可以使用绝对导入来引用同级包的子模块。例如,如果模块sound.filters.vocoder需要使用sound.effects包中的echo模块,则可以使用from sound.effects导入echo。
你也可以使用import语句的from package import 名称形式编写相对导入。这些导入使用前导点表示相对导入中涉及的当前和父程序包。例如,在surround模块中,你可以使用:
from . import echo
from .. import formats
from ..filters import equalizer
请注意,相对导入基于当前模块的名称。由于主模块的名称始终为“ __main__”,因此用作Python应用程序主模块的模块必须始终使用绝对导入。
多个目录中的包
包支持另一个特殊属性__path__。在执行该文件中的代码之前,该文件初始化为包含目录的名称的列表,该目录包含软件包的__init__.py。这个变量可以修改;这样做会影响以后对包中包含的模块和子包的搜索。
尽管通常不需要此功能,但可以使用它扩展软件包中的模块集。