3.4 导入包的本质
导入包的本质其实是“导入了包的__init__.py”文件。也就是说,”import pack1”意味着执行了包 pack1 下面的__init__.py 文件。 这样,可以在__init__.py 中批量导入我们需要的模块,而不再需要一个个导入。
【例1】
包 a 的 __init__.py 定义中,如下图:
所以实际上导入 a 包的时候就是批量导入了 turtle 和 math 两个包。
并且,再单独导入 math 对比查看 id:
import a
import math
print(id(math))
print(id(a.math))
运行结果:
分析:
由此可见,两个math是同一个对象。实际是首先在 import a 时导入了 math 模块,形成了对象, 并记为 a.math,import math 之后不过是再又把 a.math 引用的对象又引用给 math。
__init__.py 的三个核心作用:
- 作为包的标识,不能删除;
- 用来实现模糊导入;
- 导入包实质是执行__init__.py 文件,可以在__init__.py 文件中做这个包的初始化、以及需要统一执行代码、批量导入。
【例2】测试包的__init__.py 文件的用法
a 包下的__init__.py 文件内容:
import turtle
import math
print('导入a包')
b 包下的 module_B1.py 文件中导入 a 包,代码如下:
import a
print(a.math.pi)
运行结果:
分析:
可以看到,在 a 包定义中导入的 math 模块在 b 包的 module_B!.py 中运行后,将 math 识别为 a 包的子包了。
注1:如上测试我们可以看出 python 的设计者非常巧妙的通过__init__.py 文件将包转成了模块的操作。因此,可以说“包的本质还是模块”。
注2:实际上可以在__init__.py 文件中定义类、函数,但为了保证结构的统一性(类和函数的定义一般在包内其他py文件中定义),一般不这样操作,还是仅将__init__.py 文件用于批量导入模块。
3.5 用*导入包
import * 这样的语句理论上是希望文件系统找出包中所有的子模块,然后导入它们。这可能会花长时间等。Python 解决方案是提供一个明确的包索引。
这个索引由 __init__.py 定义 __all__ 变量,该变量为一列表,如上例 a 包下的 __init__.py 中,可定义:
__all__ = ['module_A1', 'module_A2']
这意味着, from a import * 会从对应的包中导入以上 module_A1 和 module_A2 两个子模块。
即由 __all__ 变量定义的列表来决定 from xx包 import * 是导入哪些子模块。
注意:
尽管提供 import * 的方法,仍不建议在生产代码中使用这种写法。
3.6 包内引用
如果是子包内的引用,可以按相对位置引入子模块。以 aa 包下的 module_AA1 中导入 a 包下内容为例:
包的结果如下图:
包内引用代码为:
from .. import module_A1 #..表示上级目录
.表示同级目录
from . import module_AA2 #.表示同级目录