如果你从Python解释器中退出并重新输入,你所做的定义(函数和变量)将会丢失。 因此,如果你想编写一个稍长的程序,最好使用文本编辑器为解释器准备输入,然后用该文件作为输入来运行它。 这被称为创建脚本。 随着程序变长,您可能需要将其分成几个文件以便于维护。 您可能还想使用您在几个程序中编写的方便功能,而不将其定义复制到每个程序中。
为了支持这一点,Python有一种方法可以将定义放在一个文件中,并在脚本或解释器的交互式实例中使用它们。
模块是一个包含Python定义和语句的文件。 文件名是带有后缀.py的模块名称。 在模块中,模块的名称(作为字符串)可用作全局变量name的值。 例如,使用您最喜欢的文本编辑器在当前目录中创建一个名为fibo.py的文件,其中包含以下内容:
# Fibonacci numbers module
def fib(n):
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
print()
def fib2(n):
result = []
a, b = 0, 1
while a < n:
result.append(a)
a, b = b, a+b
return result
现在输入Python解释器并使用以下命令导入此模块:
import fibo
这不会直接在当前符号表中输入在fibo中定义的函数的名称; 它只在那里输入模块名称fibo。 使用模块名称可以访问这些功能:
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'
如果您打算经常使用某个功能,则可以将其分配给本地名称local name
fib = fibo.fib
fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
关于模块的更多信息
一个模块可以包含可执行语句以及函数定义。 这些陈述旨在初始化模块。 它们仅在导入语句中遇到第一次模块名称时执行。 (如果文件作为脚本执行,它们也会运行。)
每个模块拥有自己的私有符号表,这个符号表被模块中定义的所有函数当成全局符号表。因此,模块作者可以使用模块中的全局符号表而不必担心与用户的全局变量发生冲突。另一方面,如果你知道你在做什么,你可以触摸一个模块的全局变量,使用相同的符号来表示它的函数,modname.itemname
模块可以导入其他模块。将所有的import语句放在模块或者脚本的开头是很常见的, 但这不是强制要求的!导入的模块名被放在导入模块的全局符号表中
有一种导入语句的变体(variant),可以将模块中的名称直接导入导入模块的符号表中。
例如:
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程序员不会使用这个工具,因为它会向解释器中引入一组未知的名称,可能会隐藏您已经定义的一些东西。
请注意,通常从模块或包中导入*的做法是不被接受的,因为它经常会导致代码的可读性差。 但是,可以使用它来保存交互式会话(interactive sessions)中的输入。
如果模块名称后跟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使用
它也可以用于从类似的效果中使用:
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(模块名)。
把模块当脚本使用
当你用python fibo.py <arguments>
运行Python模块时,模块中的代码会执行,就好像(as if)你导入它一样,但是要将name设置为“main”。 这意味着通过在你的模块的末尾添加这个代码:
if __name__ == "__main__":
import sys
fib(int(sys.argv[1]))
您可以将该文件作为脚本以及可导入模块使用,因为仅当模块作为“主”文件执行时,才会运行解析命令行的代码:
第一行是没加以上代码,可以看到什么结果都没有,而加了代码以后,就像第二行展示的,输出了结果。sys.argv[1]就是图中的50,fib就是脚本中的模块函数,当然你也可以换成fib2,但是记得加上print,否则时不会看见结果的。
如果模块已经导入了,则下面的代码不会运行import fibo
这通常用于为模块提供方便的用户界面,或者用于测试目的(在脚本执行测试套件时运行模块)。
模块搜索路径
当导入一个名为spam的模块时,解释器首先搜索具有该名称的内置模块。 如果找不到,它会在变量sys.path给出的目录列表中搜索名为spam.py的文件。 sys.path是从这些位置初始化的
- 包含输入脚本的目录(或未指定文件时的当前目录).
- PYTHONPATH(目录名列表,与shell变量PATH语法相同)。
- 依赖于安装的默认值。
初始化之后,Python程序可以修改sys.path。包含了运行脚本的目录会放在搜索路径的开始,在标准库路径之前。这意味着这个路径会相比于标准库中同样名称的模块先加载。这种做法是错误的,除非替代是故意的。更多信息参考Standard Modules。
“编译”python文件
为了加速加载模块,Python将每个模块的编译版本缓存在名为module.version.pyc的pycache目录下,其中版本对编译文件的格式进行编码; 它通常包含Python版本号。 例如,在CPython版本3.3中,spam.py的编译版本将被缓存为pycache / spam.cpython-33.pyc。 这个命名约定允许来自不同版本和不同版本的Python的编译模块共存。
Python根据编译后的版本检查源代码的修改日期,看它是否过期并需要重新编译。 这是一个完全自动的过程。 另外,编译后的模块是独立于平台的,因此可以在不同体系结构的系统之间共享相同的库。
在两种情况下,Python不会检查缓存。 首先,它总是重新编译并且不存储从命令行直接加载的模块的结果。 其次,如果没有源模块,它不会检查缓存。 要支持非源代码(仅编译)分发,编译模块必须位于源代码目录中,并且不得有源模块。
专家提示:
1. 您可以在Python命令中使用-O或-OO开关来减小已编译模块的大小。 -O开关删除断言语句,-OO开关删除断言语句和doc字符串。 由于某些程序可能依赖于这些程序,所以如果您知道自己在做什么,则只应使用该选项。 “优化”模块具有选项标签,通常较小。 未来版本可能会改变优化的效果。
2. 从.pyc文件读取时,程序运行速度不会比从.py文件读取时快; 关于.pyc文件的唯一更快的事情是它们被加载的速度。
3. 模块compileall可以为目录中的所有模块创建.pyc文件。
4. 这个过程有更多细节,包括PEP 3147中的决策流程图。
标准模块
Python提供了一个标准模块库,该库在一个单独的文件中描述:Python库参考(the Python Library Reference)。一些模块内置到解释器中,它们提供了一些操作,虽然它们不是语言的一部分,但也内置了。原因是为了提高效率或者是提供对操作系统原语(operationg system primitive),比如系统调用的访问。这些模块的集合是一个配置选项,它也取决于底层平台(underlying platform)。 例如,winreg模块仅在Windows系统上提供。 一个特定的模块值得注意:sys,每个Python解释器都内置了sys。 变量sys.ps1和sys.ps2定义用作主要和次要提示的字符串(primary and secondary prompts):
如果是在交互模式下,这两个变量被唯一定义
变量sys.path是确定模块的解释器搜索路径的字符串列表。 它被初始化为从环境变量PYTHONPATH获取的默认路径,或者如果未设置PYTHONPATH则从内置默认路径初始化。 您可以使用标准列表操作对其进行修改
“`python
import sys
sys.path.append(‘C:/Users/way’)
“`
dir()函数
内置函数dir()用于找出模块定义的名称。
“`python
import fibo, sys
dir(fibo)
“`
['__builtins__',
'__cached__',
'__doc__',
'__file__',
'__loader__',
'__name__',
'__package__',
'__spec__',
'fib',
'fib2']
“`python
dir(sys)
“`
['__displayhook__',
'__doc__',
'__excepthook__',
'__interactivehook__',
'__loader__',
'__name__',
'__package__',
'__spec__',
'__stderr__',
'__stdin__',
'__stdout__',
'_clear_type_cache',
'_current_frames',
'_debugmallocstats',
'_getframe',
'_home',
'_mercurial',
'_xoptions',
'api_version',
'argv',
'base_exec_prefix',
'base_prefix',
'builtin_module_names',
'byteorder',
'call_tracing',
'callstats',
'copyright',
'displayhook',
'dllhandle',
'dont_write_bytecode',
'exc_info',
'excepthook',
'exec_prefix',
'executable',
'exit',
'flags',
'float_info',
'float_repr_style',
'getallocatedblocks',
'getcheckinterval',
'getdefaultencoding',
'getfilesystemencoding',
'getprofile',
'getrecursionlimit',
'getrefcount',
'getsizeof',
'getswitchinterval',
'gettrace',
'getwindowsversion',
'hash_info',
'hexversion',
'implementation',
'int_info',
'intern',
'last_traceback',
'last_type',
'last_value',
'maxsize',
'maxunicode',
'meta_path',
'modules',
'path',
'path_hooks',
'path_importer_cache',
'platform',
'prefix',
'ps1',
'ps2',
'ps3',
'setcheckinterval',
'setprofile',
'setrecursionlimit',
'setswitchinterval',
'settrace',
'stderr',
'stdin',
'stdout',
'thread_info',
'version',
'version_info',
'warnoptions',
'winver']
如果没有参数,dir()会列出您当前定义的名称:
“`python
a = [1, 2, 3, 4, 5]
import fibo
fib = fibo.fib
dir()
“`
['In',
'Out',
'_',
'_1',
'_2',
'_3',
'__',
'___',
'__builtin__',
'__builtins__',
'__doc__',
'__loader__',
'__name__',
'__package__',
'__spec__',
'_dh',
'_i',
'_i1',
'_i2',
'_i3',
'_i4',
'_ih',
'_ii',
'_iii',
'_oh',
'a',
'exit',
'fib',
'fibo',
'get_ipython',
'quit']
注意,它列出来了所有名字的类型:变量,模块函数等
dir()不会列出内置函数和变量名。如果你想要那些的话,它们定义在标准模块builtins中
“`python
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',
'WindowsError',
'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']
包(packages)
包是通过“.模块名”来组织模块命名空间的一种方式。例如,模块名A.B指出模块B是包A中的一个子模块。就像模块的使用可以让不同模块的作者不必担心彼此的全局变量名一样,使用虚线模块名称可以让像NumPy或Pillow这样的多模块软件包的作者不必担心彼此的模块名称
假设你想设计一个模块集(一个“包”)来统一处理声音文件和声音数据。 有许多不同的声音文件格式(通常由其扩展名识别,例如:.wav,.aiff,.au),因此您可能需要创建和维护不断增长的模块集合,以便在各种文件格式之间进行转换。 还有很多不同的操作可能需要在声音数据上执行(例如混音,添加回声,应用均衡器功能,创建仿真立体声效果),因此除此之外,您还将编写永无止境的模块流以执行 这些操作。 以下是您的软件包的可能结构(用分层文件系统表示):
当导入包时,Python将搜索sys.path上的目录,查找包子目录。
需要init.py文件才能使Python将目录视为包含包; 这是为了防止具有通用名称的目录(如字符串)无意中隐藏掉稍后会出现的有效模块。 在最简单的情况下,init.py可以只是一个空文件,但它也可以执行包的初始化代码或设置all变量,稍后介绍。
包的用户可以从包中导入单个模块,例如:import sound.effects.echo
这样就会加载子模块import 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
这样就可以直接使用函数echofilter()
echofilter(input, output, delay=0.7, atten=4)
请注意,当使用from package import item时,the item 既可以是包中的一个子模块或者是一个子包,或者是包中定义的其他名字,像函数,类或者变量。import语句首先测试是否the item在包中定义,如果没有,就会假设它是一个模块,并尝试去加载,如果没有找到,就会引发一个ImportError 异常.
相反,使用import item.subitem.subsubitem之类的语法时,除最后一项以外的每个项都必须是一个包;
Importing * From a Package
现在当用户从sound.effects import *写入时会发生什么? 理想情况下,人们会希望以某种方式进入文件系统,查找包中存在哪些子模块,然后将它们全部导入。 这可能需要很长时间,并且导入子模块可能具有不希望的副作用,这些副作用只应在明确导入子模块时才会发生。
唯一的解决方案是软件包作者提供包的明确索引。 import语句使用以下约定:如果某个包的init.py代码定义了一个名为all的列表,则它将成为从包import 导入时应导入的模块名列表。 当软件包的新版本发布时,软件包作者需要保持该列表为最新版本。 如果软件包作者没有看到用于从包中导入的用途,那么软件包作者也可能决定不支持它。 例如,文件sound / effects / _ init_.py可以包含以下代码:
__all__ = ["echo", "surround", "reverse"]
这意味着来自sound.effects的import *会导入声音包的三个命名子模块。
如果未定义all,则来自sound.effects import *的语句不会将包sound.effects中的所有子模块导入当前名称空间; 它只确保包sound.effects已被导入(可能在init.py中运行任何初始化代码),然后导入包中定义的任何名称。 这包括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
中使用没有任何问题! 实际上,这是推荐的符号,除非导入模块需要使用不同软件包中具有相同名称的子模块。
内部包参考 Intra-package References
包被构建为子包时(与示例中的声音包一样),可以使用绝对导入来引用兄弟包的子模块。 例如,如果sound.filters.vocoder模块需要使用sound.effects包中的echo模块,则可以使用from sound.effects import echo。
您还可以使用from module import name的形式来编写相对导入。 这些导入使用前导点(leading dots)来指示相关导入中涉及的当前包和父包。 例如,从surround模块,您可以使用:
from . import echo
from .. import formats
from ..filters import equalizer
请注意,相对导入是基于当前模块的名称。 由于主模块的名称始终为“main”,因此用作Python应用程序主模块的模块必须始终使用绝对导入
多个目录的包
包还支持一个更特殊的属性path。 在执行该文件中的代码之前,它被初始化为一个列表,这个列表中包含了包含init.py的所有的目录名。 这个变量可以修改; 这样做会影响将来对包中包含的模块和子包的搜索。
虽然此功能通常不是必需的,但它可用于扩展包中的一组模块