一、什么是模块
如果你退出 Python 解释器并重新进入,你做的任何定义(变量和方法)都会丢失。因此,如果你想要编写一些更大的程序,为准备解释器输入使用一个文本编辑器会更好,并以那个文件替代作为输入执行。这就是脚本。随着你的程序变得越来越长,你可能想要将它分割成几个更易于维护的文件。你也可能想在不同的程序中使用顺手的函数,而不是把代码在它们之间中拷来拷去。
为了满足这些需要,Python 提供了一个方法可以从文件中获取定义,在脚本或者解释器的一个交互式实例中使用。这样的文件被称为 模块;模块中的定义可以 导入 到另一个模块或 主模块 中(在脚本执行时可以调用的变量集位于最高级,并且处于计算器模式)。
模块是包括 Python 定义和声明的文件。文件名就是模块名加上 .py
后缀。模块的模块名(做为一个字符串)可以由全局变量 __name__
得到。例如,你可以用自己惯用的文件编辑器在当前目录下创建一个叫 fibo.py 的文件,录入如下内容:
# -*- coding: utf-8 -*-
def fib(n):
a, b = 0, 1
while b < n:
print(b, end=' ')
a, b = b, a+b
print()
def fib2(n):
result = []
a, b = 0, 1
while b < n:
result.append(b)
a, b = b, a+b
return result
然后通过import引入该模块:
import fibo
result=fibo.fib(1000)
print(result) # 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
result=fibo.fib2(100)
print(result) # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
print(fibo.__name__) # fibo
如果打算频繁使用一个函数,你可以将它赋予一个本地变量:
fib = fibo.fib
fib(500) # 1 1 2 3 5 8 13 21 34 55 89 144 233 377
二、深入理解模块
除了包含函数定义外,模块也可以包含可执行语句。这些语句一般用来初始化模块。他们仅在 第一次 被导入的地方执行一次。
每个模块都有自己私有的符号表,被模块内所有的函数定义作为全局符号表使用。因此,模块的作者可以在模块内部使用全局变量,而无需担心它与某个用户的全局变量意外冲突。从另一个方面讲,如果你确切的知道自己在做什么,你可以使用引用模块函数的表示法访问模块的全局变量,modname.itemname
。
模块可以导入其他的模块。一个(好的)习惯是将所有的 import 语句放在模块的开始(或者是脚本),这并非强制。被导入的模块名会放入当前模块的全局符号表中。
1、impor语句
您可以通过在其他一些Python源文件执行一个import语句,来使用任何Python源文件作为一个模块。import 语句的语法如下:
import module1[, module2[,... moduleN]
当解释程序遇到import语句,如果模块存在于搜索路径它将导入这个模块。搜索路径是一个目录列表,解释器导入模块之前将进行搜索。例如,要导入模块 hello.py,需要在脚本的顶部放置下面的命令 -
#!/usr/bin/python3
# Import module support
import support
# Now you can call defined function that module as follows
support.print_func("Zara")
当执行上面的代码,它产生以下结果 -
Hello : Zara
模块仅被加载一次,不管它导入(import)的次数。如果多个导入出现,可以防止模块执行一遍又一遍。
从Python的声明语句,也可以从一个模块中导入特定的属性到当前的命名空间。from…import 的语法如下-
from modname import name1[, name2[, ... nameN]]
例如,从 fib 模块导入 fibonacci 函数,使用下面的语句-
#!/usr/bin/python3
# Fibonacci numbers module
def fib(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
>>> from fib import fib
>>> fib(100)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
此语句不导入整个 fib 模块到当前的命名空间; 它只是从 fib 模块导入全局符号表中的项目。
也可以使用下面的 import 语句, 将一个模块的所有名称导入到当前的命名空间 -
from modname import *
这提供了一种简单的方法来导入模块到当前命名空间中的所有项目; 不过,需要注意的是在实践中往往不鼓励从一个模块或包中使用 * 导入所有,因为这样会让代码变得很难读。不过,在交互式会话中这样用很方便省力。
注解
出于性能考虑,每个模块在每个解释器会话中只导入一遍。因此,如果你修改了你的模块,需要重启解释器;或者,如果你就是想交互式的测试这么一个模块,可以用 imp.reload()
重新加载,例如 import imp; imp.reload(modulename)
。
2、作为脚本来执行模块
当你使用以下方式运行 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
>>>
这通常用来为模块提供一个便于测试的用户接口(将模块作为脚本执行测试需求)。
3、模块的搜索路径
导入一个叫 spam 的模块时,解释器先在当前目录中搜索名为 spam.py 的文件。如果没有找到的话,接着会到 sys.path 变量中给出的目录列表中查找。 sys.path 变量的初始值来自如下:
1、输入脚本的目录(当前目录)。
2、环境变量 PYTHONPATH 表示的目录列表中搜索
(这和 shell 变量 PATH 具有一样的语法,即一系列目录名的列表)。
3、Python 默认安装路径中搜索。
实际上,解释器由 sys.path 变量指定的路径目录搜索模块,该变量初始化时默认包含了输入脚本(或者当前目录), PYTHONPATH 和安装目录。这样就允许 Python 程序了解如何修改或替换模块搜索目录。需要注意的是由于这些目录中包含有搜索路径中运行的脚本,所以这些脚本不应该和标准模块重名,否则在导入模块时 Python 会尝试把这些脚本当作模块来加载。这通常会引发错误。
PYTHONPATH 是一个环境变量,它由一个目录列表组成。Python路径中的语法与 shell 变量PATH相同。
下面是 Windows系统中一个典型的 PYTHONPATH :
set PYTHONPATH=c:\python34\lib;
这里是 UNIX 系统的典型 PYTHONPATH :
set PYTHONPATH=/usr/local/lib/python
4、命名空间和范围界定
变量名称(标识)映射到对象。命名空间是一组变量(键)及其相应的对象(值)的字典。
使用Python编写的语句可以在本地命名空间和全局命名空间访问变量。如果本地和全局变量具有相同的名称,局部变量将覆盖全局变量。每个函数都有它自己的本地名称空间。 类方法与普通的函数遵循相同的范围规则。
Python作出明智的猜测变量是局部还是全局的。它假定任何变量赋值在函数中的值是局部的。因此,为了给一个函数内给一个全局变量赋值, 您必须使用global语句。
例如,我们在全局命名空间定义一个变量Money。在函数AddMoney中使用了局部变量 Money,因此在AddMoney中 Python 假定 Money 作为一个局部变量。 然而,我们在设置初始值之前进行其相关计算操作, 所以报一个 UnboundLocalError。
#!/usr/bin/python3
Money = 2000
def AddMoney():
# Uncomment the following line to fix the code:
# global Money
Money = Money + 1
print (Money)
AddMoney()
print (Money)
上述代码输出:
Traceback (most recent call last):
File "E:/PycharmProjects/mytest.py", line 8, in <module>
AddMoney()
File "E:/PycharmProjects/mytest.py", line 5, in AddMoney
Money = Money + 1
UnboundLocalError: local variable 'Money' referenced before assignment
解决上述错误的方法是在AddMoney把Money声明为全局变量,使用:global Money
5、标准模块
Python 带有一个标准模块库,并发布有独立的文档,名为 Python 库参考手册(此后称其为“库参考手册”)。有一些模块内置于解释器之中,这些操作的访问接口不是语言内核的一部分,但是已经内置于解释器了。这既是为了提高效率,也是为了给系统调用等操作系统原生访问提供接口。这类模块集合是一个依赖于底层平台的配置选项。例如,winreg
模块只提供在 Windows 系统上才有。有一个具体的模块值得注意: sys
,这个模块内置于所有的 Python 解释器。
变量 sys.path
是解释器模块搜索路径的字符串列表。它由环境变量 PYTHONPATH 初始化,如果没有设定 PYTHONPATH ,就由内置的默认值初始化。
三、dir() 函数
内置函数 dir() 用于按模块名搜索模块定义,它返回一个字符串类型的存储列表:
import fibo,sys
result=dir(fibo)
print(result)
result=dir(sys)
print(result)
结果如下:
['__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', '_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', 'get_coroutine_wrapper', 'getallocatedblocks', 'getcheckinterval', 'getdefaultencoding', 'getfilesystemencoding', 'getprofile', 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettrace', 'getwindowsversion', 'hash_info', 'hexversion', 'implementation', 'int_info', 'intern', 'is_finalizing', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'set_coroutine_wrapper', 'setcheckinterval', 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', 'thread_info', 'version', 'version_info', 'warnoptions', 'winver']
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', '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']
globals() 和 locals()函数可用于在全局和局部名字空间返回名称,取决于从哪里调用它们。
如果 locals() 从函数中调用, 它会返回所有可以从函数访问的名字。
如果 globals() 在一个函数中调用,它将返回所有可以在全局范围内,可从函数访问的名字。两种这些函数的返回类型是字典。因此,名称可以使用 keys() 函数来提取。
四、Python包
1、基本使用
包通常是使用用“圆点模块名”的结构化模块命名空间。例如,名为 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 package import item 方式导入包时,这个子项(item)既可以是包中的一个子模块(或一个子包),也可以是包中定义的其它命名,像函数、类或变量。import 语句首先核对是否包中有这个子项,如果没有,它假定这是一个模块,并尝试加载它。如果没有找到它,会引发一个 ImportError 异常。
相反,使用类似 import item.subitem.subsubitem 这样的语法时,这些子项必须是包,最后的子项可以是包或模块,但不能是前面子项中定义的类、函数或变量。
2、包内引用
如果包中使用了子包结构(就像示例中的 sound 包),可以按绝对位置从相邻的包中引入子模块。例如,如果 sound.filters.vocoder 包需要使用 sound.effects 包中的 echo 模块,它可以 from sound.Effects import echo。
你可以用这样的形式 from module import name 来写显式的相对位置导入。那些显式相对导入用点号标明关联导入当前和上级包。以 surround 模块为例,你可以这样用:
from . import echo
from .. import formats
from ..filters import equalizer
需要注意的是显式或隐式相对位置导入都基于当前模块的命名。因为主模块的名字总是 “__main__
“,Python 应用程序的主模块应该总是用绝对导入。
3、多重目录中的包
包支持一个更为特殊的特性, __path__
。 在包的 __init__.py
文件代码执行之前,该变量初始化一个目录名列表。该变量可以修改,它作用于包中的子包和模块的搜索功能。
这个功能可以用于扩展包中的模块集,不过它不常用。