这篇文章会介绍 Python 中的模块和包。
Python - 模块与包
引申出模块化编程的概念,即将代码分解成小模块,各自解决不同的问题,这样使得代码更简单 (Simplicity),容易维护 (Maintainability),也更容易分享、重用代码 (Reusability)。
Python 中的模块主要有三种:
- 用Python写的模块
- 用C 写的模块
- 内置的模块如
itertools
模块
这篇文章只关注 Python 写的模块。
模块的搜索路径
假设你写了个模块 mod.py
, 怎么使用呢?
如果你当前工作目录在合适的位置,你可以执行 import mod
, 然后你便可以引用其中的对象,那什么叫合适的位置?
- 与
mod.py
同一位置 - 你的模块位置被包含在
PYTHONPATH
中 - 模块位置在安装 Python 时标准链接库目录是定义过
- 你创建了一个
.pth
文件告诉 Python 去哪里找,此模块位于其中
上面四种情况,只有 PYTHONPATH
环境变量和路径文件可以被用户配置。
Python 会自动去寻找这些地方是否有你需要的包,你可以通过 sys.path
查看它的搜索顺序。
所以还有一种方法导入包,即将你的包地址放入 sys.path
,它是一个列表,所以你可以执行:
1 | sys.path.append(r'C:\Users\john') |
当模块被导入后,你也可以通过 mod.__file__
查看包的地址。
1 | import mod |
导入模块
模块的存在是为了被导入 import
以使用, 就像钢琴的存在是被人演奏以产生音乐。
import module_name
import
上面已有例子,需注意 import
不能让你直接使用所有模块内的内容。模块有其私有符号表 (private symbol table), 模块以此来确定各自之间的边界。
举个例子:
1 | import zoo |
from module_name import name
另一种导入模块的方式是直接导入模块内的对象:
1 | from <module_name> import <name(s)> |
需要小心 from <module_name> import *
这种方式,除非你很清楚自己在做什么。
from module_name import name as alt_name
这种方式和上面的一样,只是多了 as
关键字以定义别名。
导入提示
导入模块的语句通常写在文件首部,但也可以写在函数中,不过 Python3 不允许在函数中 import *
。
防止模块导入失败,可以使用 try...except...
语句捕捉 ImportError
错误。
dir()
函数可以返回当前命名空间中所有的变量。
导入模块的时候,模块的代码将会执行,如果希望模块内的某些代码只是在你需要的时候才调用执行,可以加入以下代码:
1 | if (__name__ == '__main__'): |
导入模块的操作只会执行一次,考虑以下代码:
1 | import mod |
发现第二次和第三次导入并未输出,如果想要每次导入操作都重新导入,可利用 importlib
实现。
1 | import mod |
模块可以被当作脚本执行,只需要在模块所在文件夹增加一个 __main__.py
文件。
Python 包
包是一组或多组模块,包可以让你通过包的名字简单的访问其下面的各种模块。
下面是一个包的结构,其中有两个模块。 1
2
3
4C:.
└─pkg
mod1.py
mod2.pypkg
上级目录执行 import pkg
该包里将不会包括任何东西,无法引用 mod1 也无法引用 mod2 中的内容。
__init__.py
如果在 pkg 目录下新增 __init__.py
文件,则可以引用 __init__
中的的对象。
1 | C:. |
在内部 mod1 模块中,你也可以访问__init__
中的对象。
如果想在外部通过 import pkg
的方式,通过 pkg
访问 mod1
或 mod2
中的对象,则需要在 __init__
中导入 mod1
和 mod2
。
1 | # In __init__.py |
1 | # 访问 Mod1 和 Mod2 |
在 Python3.3 之后,Implicit Namespace Packages 发布了,定义包也可以不新建
__init__
了。
__all__
你肯定写过 from <package_name> import *
这样的代码,其中 * 代表的是 import 什么?
如果在包的 __init__.py
中定义 __all__
来进行控制。
pkg/__init__.py
1 | __all__ = [ |
则所有其中的内容都会在写 from pkg import *
的时候,自动导入。
在模块中定义 __all__
亦是。
pkg/mod1.py
1 | __all__ = ['foo'] |
区别是:
- 对于一个包,如果
__all__
没有定义,import *
不会导入任何对象 - 对于一个模块,如果
__all__
没有定义,import *
默认导入所有对象
Python 子包
包中可以放子包。
1 | C:. |
使用方式则需要一层一层使用 .
语法下钻:
1 | import pkg.sub_pkg1.mod1 |
如果是兄弟模块之间要导入模块怎么办?有两种方式:
全路径:
1 | def baz(): |
相对路径: 1
2
3
4
5
6
7
8
9
10
11def baz():
print('[mod3] baz()')
class Baz:
pass
from .. import sub_pkg1
print(sub_pkg1)
from ..sub_pkg1.mod1 import foo
foo()