这一章主要是叙述了python模块的概念以及包的概念,还有它们的使用;收获也是大大的。
提起python文件,经常会听到3个名词,python脚本,python模块,python包。脚本的概念是从python交互式命令行引用过来的,把在命令行内运行的代码段复制到一个文件里再运行,这个文件就可以称为一个脚本;脚本之间可能存在相同的函数等,为了一个脚本能使用另一个脚本里的函数等,这里的另一个脚本就可以称为一个模块,也就是说一个模块里的定义是可以被导入到别的模块或者主模块中使用的;python包是python管理模块命名空间的一种方式,类似python模块解决了不同模块之间的全局变量的重名,python包解决了多个模块组成的包时模块名的重名。
4.1 模块
模块是包含 Python 定义和声明的文件。文件名就是模块名加上.py 后缀。可以通过可以全局变量 __name__ 的值得到。每个模块都有自己的私有符号表(程序源代码中的每个标识符都和它的声明或使用信息绑定在一起,比如其数据类型、作用域以及内存地址。)。模块里的所有定义把这个表当成全局符号表。所以这个模块的使用者可以在这个模块内使用全局变量而不需要担心与用户的全局变量冲突。
4.1.1 模块的导入
模块的导入语句一般常规性的会放在一个模块的最开始部分;被导入的模块名就会被放置到使用该模块的模块的全局符号表内。
模块的导入有2种方法:
A:import modname.submodname。其中这种方式只能导入包,而且只有父包的包名会被放置到引入该模块的模块的全局符号内,也就是说通过这个方式导入了包,如果要使用包内的函数或者一些变量,需要进入父包,再进入父包内的符号表内,得到那些定义。
下面有2个例子:第一个例子,python的安装子目录下有一个pip包,下面有子包以及子模块等,接下来会以这个包为例进行说明:
>>> print dir()
['__builtins__', '__doc__', '__name__', '__package__']
>>> import pip
>>> print dir()
['__builtins__', '__doc__', '__name__', '__package__', 'pip']
>>> import pip.index
>>> print dir()
['__builtins__', '__doc__', '__name__', '__package__', 'pip']
>>> import pip.index.egg_info_matches
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named egg_info_matches
在使用import语句导入该模块后,当前的全局符号表内多了包名pip;待再次导入子模块,全局符号表并没有增加子模块的名字;用import语句企图导入子文件内的函数时,提示不存在这个模块。
例2是通过这类导入,如何使用包内各种定义的问题。因为导入后当前的全局符号表内,只有包名,所以要使用子模块的方法,必须通过.的方式,从包到子模块,再到具体的方法或者其他定义。
# Fibonacci numbers module def fib(n): # write Fibonacci series up to n
a, b = ,
while b < n:
print b,
a, b = b, a+b def fib2(n): # return Fibonacci series up to n
result = []
a, b = ,
while b < n:
result.append(b)
a, b = b, a+b
return result
>>> import fibo
>>> fibo.fib() >>> fibo.fib2()
[, , , , , , , , , , ]
B. from modname.submodname import subsubmodname or function
通过这个方法,可以直接导入一个包下的子模块,或者直接导入子模块下的函数。在例子中,企图在import语句后导入子文件的函数名,会提示存在语法错误。但是导入子文件或者函数是没问题的。
>>> print dir()
['__builtins__', '__doc__', '__name__', '__package__']
>>> from pip import index
>>> print dir()
['__builtins__', '__doc__', '__name__', '__package__', 'index']
>>> from pip import index.egg_info_matches
File "<stdin>", line
from pip import index.egg_info_matches
^
SyntaxError: invalid syntax
>>> from pip.index import egg_info_matches
>>> print dir()
['__builtins__', '__doc__', '__name__', '__package__', 'egg_info_matches', 'inde
x']
如果使用from pip import index语句,要调用index内函数时,使用index.egg_info_matches进行调用。
如果使用from pip.index import egg_info_matches语句,要调用index内egg_info_matches函数时,直接使用egg_info_matches即可,但是如果企图调用该模块内的其他函数,会报错。
4.1.2 dir()函数
dir()是内置函数里的一个,是用来列出模块中定义了哪些名字,然后返回一个list字符串。里面包含变量名,模块名,函数名等等。使用help查看dir的使用,可以得到更多的小细节。
4.1.3 模块的搜索路径
导入一个模块时,python是怎么搜索这个模块的呢?首先,解析器会从内置模块内查找是否存在,如果不存在,就会从sys.path这个路径下进行搜索,这个路径包含以下三个部分:
A. 包含当前文件的文件夹
B. python配置的path下
C. python的默认安装目录下
脚本所在的目录被放置在搜索路径的最开始,也就是在标准库的路径之前。这意味着将会加载当前目录中的脚本,如果库目录中具有相同名称的模块则不会被加载。除非是有意想替换标准库,否则这应该被当成是一个错误。
python的sys.path是可以被修改的,但是其他文件运行时不会使用到该列表(或者执行完后再打印sys.path之前添加的目录就消失了)。
>>> import sys
>>> sys.path.append('/ufs/guido/lib/python')
4.1.4 标准模块
python 官网内有一个文档“Library Reference”是介绍标准模块的,标准模块里有一部分的模块是直接构建在解释器内,使用import后就能直接使用,常见的有以下几个:
4.2 包
包的基本概念在介绍模块的时候已经提及了,为了让python能识别一个目录是python包,需要在目录里增加__init__.py文件,这个文件可以是空的,可以对包进行初始化或者设置__all__
参数。
设置_all__参数的主要原因是:为包提供显式的索引。import 语句使用以下约定: 如果包中的 __init__.py 代码定义了一个名为__all__的列表,那么在遇 到 from package import *语句的时候,应该把这个列表中的所有模块名字导入。当包有新版本包发布时,就需要包的作者更新这个列表了。如果包的作者认为不可以用 import * 方式导入它们的包,也可以决定不支持它。如果 __all__ 没有定义,From a Package Importing * 语句 不会从包中导入所有的子模块到当前命名空间;它只保证包已经被导入。(在pip的该文件内未找到_all__参数的赋值,但是导入的时候却能全部导入;自己测试的时候,不给_all__参数赋值,确实又只能导入包。暂时还未解,可能做了另一种等效的赋值)
导入模块或者方法时,还有一种方法:From a Package Importing * ,对PIP包进行尝试结果如下:
>>> print dir()
['__builtins__', '__doc__', '__name__', '__package__']
>>> from pip import *
>>> print dir()
['CommandError', 'ConfigOptionParser', 'FrozenRequirement', 'InsecureRequestWarning', 'InstallationError', 'PipError', 'UpdatingDefaultsHelpFormatter', '__builtins__', '__doc__', '__name__', '__package__', 'absolute_import', 'autocomplete', 'basecommand', 'baseparser', 'bazaar', 'check_isolated', 'cmdoptions', 'commands', 'commands_dict', 'compat', 'create_main_parser', 'deprecation', 'dist_is_editable', 'download', 'exceptions', 'get_installed_distributions', 'get_prog', 'get_similar_commands', 'get_summaries', 'git', 'index', 'locale', 'locations', 'logger', 'logging', 'main', 'mercurial', 'models', 'operations', 'optparse', 'os', 'parseopts', 'pep425tags', 'pip', 're', 'req', 'status_codes', 'subversion', 'sys', 'utils', 'vcs', 'warnings', 'wheel']
结果PIP下所有的子包和子模块全部都导进来了。