模块(module)和 包(package)是Python用于组织大型程序的利器。html
模块 是一个由 变量、函数、类 等基本元素组成的功能单元,设计良好的模块一般是高内聚、低耦合、可复用、易维护的。包 是管理模块的容器,它具备 可嵌套性:一个包能够包含模块和其余包。从文件系统的视角来看,包就是目录,模块就是文件。python
从本质上讲,一个模块就是一个独立的名字空间(namespace),单纯的多个模块只能构成扁平结构的名字空间集;而包的可嵌套性,使得多个模块能够呈现出多层次结构的名字空间树。linux
若是要在一个模块A中使用另外一个模块B(即访问模块B的属性),则必须首先 导入 模块B。此时模块A称为导入模块(即importer),而模块B称为被导入模块(即importee)。shell
导入语句(import statement)有两种风格:import <>
和from <> import
。对模块的导入同时支持这两种风格。缓存
1)导入模块module(重命名为name)markdown
import module [as name]
app
>>> import sys >>> sys.version '2.7.3 (default, Apr 10 2013, 05:46:21) \n[GCC 4.6.3]' >>> import sys as s >>> s.version '2.7.3 (default, Apr 10 2013, 05:46:21) \n[GCC 4.6.3]'
2)导入模块module1(重命名为name1),模块module2(重命名为name2),等等函数
import module1 [as name1], module2 [as name2], ...
布局
>>> import sys, os >>> sys.platform, os.name ('linux2', 'posix') >>> import sys as s, os as o >>> (s.platform, o.name) ('linux2', 'posix')
3)从模块module中导入属性attribute(重命名为name)ui
from module import attribute [as name]
>>> from sys import executable >>> executable '/usr/bin/python' >>> from sys import executable as exe >>> exe '/usr/bin/python'
4)从模块module中导入属性attribute1(重命名为name1),属性attribute2(重命名为name2),等等
from module import attribute1 [as name1], attribute2 [as name2], ...
>>> from sys import platform, executable >>> platform, executable ('linux2', '/usr/bin/python') >>> from sys import platform as plf, executable as exe >>> plf, exe ('linux2', '/usr/bin/python')
5)从模块module中导入属性attribute1(重命名为name1),属性attribute2(重命名为name2),等等
from module import (attribute1 [as name1], attribute2 [as name2], ...)
>>> from sys import (platform, executable) >>> platform, executable ('linux2', '/usr/bin/python') >>> from sys import (platform as plf, executable as exe) >>> plf, exe ('linux2', '/usr/bin/python')
6)从模块module中导入全部属性
from module import *
>>> from sys import * >>> platform, executable ('linux2', '/usr/bin/python')
如下是在Python程序中推荐使用的导入语句:
import module [as name]
(导入单个模块)from module import attribute [as name]
(导入单个属性)from module import attribute1 [as name1], attribute2 [as name2], ...
(导入较少属性时,单行书写)from module import (attribute1 [as name1], attribute2 [as name2], ...)
(导入较多属性时,分行书写)应当尽可能避免使用的导入语句是:
import module1 [as name1], module2 [as name2], ...
它会下降代码的可读性,应该用多个import module [as name]
语句代替。
from module import *
它会让importer的名字空间变得不可控(极可能一团糟)。
一个 模块 就是一个Python源码文件。若是文件名为mod.py,那么模块名就是mod。
模块的导入和使用都是借助模块名来完成的,模块名的命名规则与变量名相同。
模块属性 是指在模块文件的全局做用域内,或者在模块外部(被其余模块导入后)能够访问的全部对象名字的集合。这些对象名字构成了模块的名字空间,这个名字空间其实就是全局名字空间(参考 名字空间与做用域)。
模块的属性由两部分组成:固有属性 和 新增属性。能够经过 M.__dict__ 或 dir(M) 来查看模块M的属性。
1)固有属性
固有属性 是Python为模块默认配置的属性。
例如,新建一个空文件mod.py:
$ touch mod.py $ python ... >>> import mod # 导入模块mod >>> mod.__dict__ # 模块mod的属性全貌 {'__builtins__': {...}, '__name__': 'mod', '__file__': 'mod.pyc', '__doc__': None, '__package__': None} >>> dir(mod) # 只查看属性名 ['__builtins__', '__doc__', '__file__', '__name__', '__package__']
上述示例中,空模块mod的全部属性都是固有属性,包括:
__builtins__
内建名字空间(参考 名字空间)__file__
文件名(对于被导入的模块,文件名为绝对路径格式;对于直接执行的模块,文件名为相对路径格式)__name__
模块名(对于被导入的模块,模块名为去掉“路径前缀”和“.pyc后缀”后的文件名,即os.path.splitext(os.path.basename(__file__))[0]
;对于直接执行的模块,模块名为__main__
)__doc__
文档字符串(即模块中在全部语句以前第一个未赋值的字符串)__package__
包名(主要用于相对导入,请参考 PEP 366)2)新增属性
新增属性 是指在模块文件的顶层(top-level),由赋值语句(如import、=、def和class)建立的属性。
例如,修改文件mod.py为:
#!/usr/bin/env python # -*- coding: utf-8 -*- '''this is a test module''' import sys debug = True _static = '' class test_class(object): def say(self): pass def test_func(): var = 0
再次查看模块mod的属性:
>>> import mod >>> dir(mod) ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '_static', 'debug', 'sys', 'test_class', 'test_func']
对比上一小节可知,除开固有属性外的其余属性都是新增属性,包括:
这些属性的共同点是:它们都在模块文件的顶层建立。相比之下,类方法say(在类test_class内部建立)和局部变量var(在函数test_func内部建立)都不在顶层,所以不在新增属性之列。(做为一个例子,'''this is a test module'''就是模块mod的文档字符串,即mod.__doc__
的值)
在 『导入语句』 中描述的基本语法能够概括为三类:
import module
在导入模块(importer)中,能够经过module.*的形式访问模块module中的全部属性from module import attribute
只能经过名字attribute访问模块module中的指定属性module.attributefrom module import *
能够直接经过属性名访问模块module中的全部 公有属性换句话说,模块的 公有属性 就是那些能够经过from module import *
被导出给其余模块直接使用的属性。
模块的公有属性有如下特色:
__all__
,其中包含全部可导出的公有属性的字符串名称,从而实现对公有属性的定制__all__
,那么默认全部不如下划线“_”开头的属性都是可导出的公有属性以 『新增属性』 中的mod.py为例,没有定义__all__
的公有属性:
>>> dir() # 导入模块mod前的名字空间 ['__builtins__', '__doc__', '__name__', '__package__'] >>> from mod import * # 导入模块mod中的全部公有属性 >>> dir() # 导入模块mod后的名字空间 ['__builtins__', '__doc__', '__name__', '__package__', 'debug', 'sys', 'test_class', 'test_func']
对比导入模块mod先后的状况可知,模块mod中的sys、debug、test_class和test_func都属于公有属性,由于它们的名字不以“_”开头;而其余以“_”开头的属性(包括全部“固有属性”,以及“新增属性”中的_static)都不在公有属性之列。
若是在模块文件mod.py中顶层的任何位置,增长定义一个特殊列表__all__ = ['sys', '_static', 'test_func']
(此时__all__
也是模块属性),那么此时的公有属性:
>>> dir() # 导入模块mod前的名字空间 ['__builtins__', '__doc__', '__name__', '__package__'] >>> from mod import * # 导入模块mod中的全部公有属性 >>> dir() # 导入模块mod后的名字空间 ['__builtins__', '__doc__', '__name__', '__package__', '_static', 'sys', 'test_func']
能够看出,只有在__all__
中指定的属性才是公有属性。
模块能够在命令行下被直接执行,以模块mod(对应文件mod.py)为例:
1)以脚本方式执行
python mod.py <arguments>
2)以模块方式执行
python -m mod <arguments>
一个 包 就是一个含有__init__.py文件的目录。
包与模块之间的包含关系是:一个包能够包含子包或子模块,但一个模块却不能包含子包和子模块。
与模块名相似,包名的命名规则也与变量名相同。
此外,须要特别注意的是:若是在同一个目录下,存在两个同名的包和模块,那么导入时只会识别包,而忽略模块。(参考 specification for packages 中的 『What If I Have a Module and a Package With The Same Name?』)
例如,在目录dir下新建一个文件spam.py(即模块spam),此时import spam
会导入模块spam:
$ cd dir/ $ touch spam.py $ python ... >>> import spam >>> spam <module 'spam' from 'spam.py'>
若是在目录dir下再新建一个含有__init__.py文件的目录spam(即包spam),此时import spam
则会导入包spam(而再也不是模块spam):
$ mkdir spam && touch spam/__init__.py $ python ... >>> import spam >>> spam <module 'spam' from 'spam/__init__.py'>
包属性与模块属性很是类似,也分为 固有属性 和 新增属性。
1)固有属性
与模块相比,包的 固有属性 仅多了一个__path__
属性,其余属性彻底一致(含义也相似)。
__path__
属性即包的路径(列表),用于在导入该包的子包或子模块时做为搜索路径;修改一个包的__path__
属性能够扩展该包所能包含的子包或子模块。(参考 Packages in Multiple Directories)
例如,在dir目录下新建一个包pkg(包含一个模块mod),显然在包pkg中只能导入一个子模块mod:
$ mkdir pkg && touch pkg/__init__.py $ touch pkg/mod.py $ python ... >>> import pkg.mod # 能够导入子模块mod >>> import pkg.mod_1 # 不能导入子模块mod_1 Traceback (most recent call last): File "<stdin>", line 1, in <module> ImportError: No module named mod_1
若是在dir目录下再新建一个包pkg_1(包含一个模块mod_1):
$ mkdir pkg_1 && touch pkg_1/__init__.py $ touch pkg_1/mod_1.py
而且在pkg/__init__.py中修改包pkg的__path__
属性:
print('before:', __path__) __path__.append(__path__[0].replace('pkg', 'pkg_1')) # 将“包pkg_1所在路径”添加到包pkg的__path__属性中 print('after:', __path__)
此时,在包pkg中就能够导入子模块mod_1(仿佛子模块mod_1真的在包pkg中):
$ python ... >>> import pkg.mod # 能够导入子模块mod ('before:', ['pkg']) ('after:', ['pkg', 'pkg_1']) >>> import pkg.mod_1 # 也能够导入子模块mod_1
2)新增属性
包的 新增属性 包括两部分:静态的新增属性和动态的新增属性。
静态的新增属性是指:在__init__.py的顶层(top-level),由赋值语句(如import、=、def和class)建立的属性。这部分与模块的新增属性一致。
动态的新增属性是指:在执行导入语句后动态添加的新增属性。具体而言,若是有一个导入语句导入了某个包pkg中的子模块submod(或子包subpkg),那么被导入的子模块submod(或子包subpkg)将做为一个属性,被动态添加到包pkg的新增属性当中。
以包含模块mod的包pkg为例:
>>> import pkg >>> dir(pkg) ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__'] >>> import pkg.mod # 该语句导入了包pkg中的模块mod >>> dir(pkg) # mod成为了包pkg的“动态的新增属性” ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'mod']
在公有属性方面,包与模块的行为彻底一致,固然别忘了包还有 动态的新增属性。
1)导入语句
加入包的概念之后,导入语句的风格(与仅有模块时相比)不变,可是语法上有一些细微差别:用“.”来表示包与模块之间的包含关系;可操做的对象扩充为 包、模块 和 属性。
下面是涉及包时,一些典型的导入语句:
import package
import package.module
import package.subpackage
import package.subpackage.module
from packagae import module
from packagae import subpackagae
from packagae import subpackagae.module
或from packagae.subpackagae import module
from packagae.module import attribute
from packagae.subpackagae.module import attribute
2)__init__.py
关于包的__init__.py,有如下几点总结:
__all__
、__path__
等)模块与包的导入原理几乎彻底一致,所以下面以模块为主进行讨论,仅在有显著差别的地方对包做单独说明。
对于模块M而言,根据导入语句的不一样(指明了模块M是否在一个包中),可能存在导入依赖的问题:
import M
模块M不在一个包中,所以无导入依赖:直接以“M”为 完整名(fully qualified name)导入模块M
import A.B.M
或者from A.B import M
模块M在一个子包B中,而子包B又在一个包A中,所以存在导入依赖:会首先以“A”为 完整名 导入包A,接着以“A.B”为 完整名 导入子包B,最后以“A.B.M”为 完整名 导入模块M。
一个模块的导入过程主要分三步:搜索、加载 和 名字绑定。(具体参考 The import statement)
1)搜索
搜索 是整个导入过程的核心,也是最为复杂的一步。对于被导入模块M,按照前后顺序,搜索的处理步骤为:
import P.M
),则以P.__path__
为搜索路径进行查找;若是模块M不在一个包中(如import M
),则以 sys.path 为搜索路径进行查找2)加载
正如 『搜索』 步骤中所述,对于找到的模块M:若是M在缓存 sys.modules 中,则直接返回;不然,会加载M。
加载 是对模块的初始化处理,包括如下步骤:
__name__
、__file__
、__package__
和__loader__
(对于包,则还有__path__
)有一点值得注意的是,加载不仅是发生在导入时,还能够发生在 reload() 时。
3)名字绑定
加载完importee模块后,做为最后一步,import语句会为 导入的对象 绑定名字,并把这些名字加入到importer模块的名字空间中。其中,导入的对象 根据导入语句的不一样有所差别:
import obj
,则对象obj能够是包或者模块from package import obj
,则对象obj能够是package的子包、package的属性或者package的子模块from module import obj
,则对象obj只能是module的属性根据 The import statement 中的描述,如下是导入原理对应的Python伪码:
import sys import os.path def do_import(name): '''导入''' parent_pkg_name = name.rpartition('.')[0] if parent_pkg_name: parent_pkg = do_import(parent_pkg_name) else: parent_pkg = None return do_find(name, parent_pkg) def do_find(name, parent_pkg): '''搜索''' if not name: return None # step 1 if name in sys.modules: return sys.modules[name] else: # step 2 for finder in sys.meta_path: module = do_load(finder, name, parent_pkg) if module: return module # step 3 src_paths = parent_pkg.__path__ if parent_pkg else sys.path for path in src_paths: if path in sys.path_importer_cache: finder = sys.path_importer_cache[path] if finder: module = do_load(finder, name, parent_pkg) if module: return module else: # handled by an implicit, file-based finder else: finder = None for callable in sys.path_hooks: try: finder = callable(path) break except ImportError: continue if finder: sys.path_importer_cache[path] = finder elif os.path.exists(path): sys.path_importer_cache[path] = None else: sys.path_importer_cache[path] = # a finder which always returns None if finder: module = do_load(finder, name, parent_pkg) if module: return module raise ImportError def do_load(finder, name, parent_pkg): '''加载''' path = parent_pkg.__path__ if parent_pkg else None loader = finder.find_module(name, path) if loader: return loader.load_module(name) else: return None
正如 『导入过程』 中所述,sys.path是 不在包中的模块(如import M)的“搜索路径”。在这种状况下,控制sys.path就能控制模块的导入过程。
sys.path 是一个路径名的列表,按照前后顺序,其中的路径主要分为如下四块:
os.environ['PYTHONPATH']
(相似shell环境变量PATH)为了控制sys.path,能够有三种选择:
关于导入,还有一点很是关键:加载只在第一次导入时发生。这是Python特地设计的,由于加载是个代价高昂的操做。
一般状况下,若是模块没有被修改,这正是咱们想要的行为;但若是咱们修改了某个模块,重复导入不会从新加载该模块,从而没法起到更新模块的做用。有时候咱们但愿在 运行时(即不终止程序运行的同时),达到即时更新模块的目的,内建函数 reload() 提供了这种 从新加载 机制。
关键字reload
与import
不一样:
import
是语句,而reload
是内建函数import
使用 模块名,而reload
使用 模块对象(即已被import语句成功导入的模块)从新加载(reload(module))有如下几个特色:
import M
语句导入的模块M:调用reload(M)
后,M.x
为 新模块 的属性x(由于更新M后,会影响M.x
的求值结果)from M import x
语句导入的属性x:调用reload(M)
后,x
仍然是 旧模块 的属性x(由于更新M后,不会影响x
的求值结果)reload(M)
后,从新执行import M
(或者from M import x
)语句,那么M.x
(或者x
)为 新模块 的属性x严格来讲,模块(或包)的导入方式分为两种:绝对导入 和 相对导入。以上讨论的导入方式都称为 绝对导入,这也是Python2.7的默认导入方式。相对导入是从Python2.5开始引入的,主要用于解决“用户自定义模块可能会屏蔽标准库模块”的问题(参考 Rationale for Absolute Imports)。
相对导入 使用前导的“.”来指示importee(即被导入模块或包)与importer(当前导入模块)之间的相对位置关系。相对导入 只能使用from <> import
风格的导入语句,import <>
风格的导入语句只能用于 绝对导入。(相对导入的更多细节,请参考 PEP 328)
例若有一个包的布局以下:
pkg/ __init__.py subpkg1/ __init__.py modX.py modY.py subpkg2/ __init__.py modZ.py modA.py
假设当前在文件modX.py或subpkg1/__init__.py中(即当前包为subpkg1),那么下面的导入语句都是相对导入:
from . import modY # 从当前包(subpkg1)中导入模块modY from .modY import y # 从当前包的模块modY中导入属性y from ..subpkg2 import modZ # 从当前包的父包(pkg)的包subpkg2中导入模块modZ from ..subpkg2.modZ import z # 从当前包的父包的包subpkg2的模块modZ中导入属性z from .. import modA # 从当前包的父包中导入模块modA from ..modA import a # 从当前包的父包的模块modA中导入属性a
与绝对导入不一样,相对导入的导入原理比较简单:根据 模块的__name__
属性 和 由“.”指示的相对位置关系 来搜索并加载模块(参考 Relative Imports and __name__)。
因为相对导入会用到模块的__name__
属性,而在直接执行的主模块中,__name__
值为__main__
(没有包与模块的信息),因此在主模块中:尽可能所有使用绝对导入。
若是非要使用相对导入,也能够在顶层包(top-level package)的外部目录下,以模块方式执行主模块:python -m pkg.mod
(假设顶层包为pkg,mod为主模块,其中使用了相对导入)。(具体参考 PEP 366)