在 Python 的发展历程中,import 机制经历了多次重要的变更和演进。各种细节复杂无比,让人倍感头疼...
例子
通过一些小例子梳理一下?
注意,本文所用环境:Windows 下 Python3.12。
例子1:先认识一下关键词
- import
 - from
 - as
 *
随便导入一个标准库:
1 2 3 4 5 6 7 8 9 10  |  | 
文件保存在 a1.py,四种方式均导入同一个Module,直接执行
1 |  | 
结果如下:
1 2 3 4  |  | 
除了 from xxx import * 一般不建议使用之外,其他可以灵活使用。
每个模块都有 __file__ 和 __package__ 两个属性,可以输出:
1 2  |  | 
结果类似下面:
1 |  | 
嗯,__package__ 可以为空。
例子2:简单模组导入
文件系统结构:
1 2  |  | 
定义一个 Module m1.py:
1 2  |  | 
同目录下的应用程序 a2.py 中导入并使用它:
1 2 3 4 5 6 7 8 9  |  | 
注意:不是相对路径导入!!之所以能找到 m1.py,是因为 a2.py 执行时其所在路径被加入到了 sys.path 路径中,可以输出看看:
1 2  |  | 
结果类似下面这样:
1 |  | 
当然,如果愿意,也可以把m1.py丢到上面列出的其他文件夹中,或者直接放到哪个zip文件里面。
例子3:简单的包导入
文件系统结构:
1 2  |  | 
创建文件夹p1并定义一个包文件 p1/__init__.py:
1 2  |  | 
在p1的同级目录下的应用程序 a3.py 中导入并使用它:
1 2 3 4 5 6 7  |  | 
和前面的使用没有差异,只不过它 多 了一个属性__path__,用于定位包内的子包或模块。
例子4:简单的命名空间包导入
文件系统结构:
1 2 3  |  | 
在两个不同的位置下的p2文件夹中,分别创建
- p2/addition.py
 
1 2  |  | 
- p2/subtraction.py
 
1 2  |  | 
在d1、d2的同级目录下的应用程序 a4.py 中导入并使用它:
1 2 3 4 5 6 7 8 9 10 11 12  |  | 
注意,p2包的所有路径都需要添加的sys.path中。
输出结果(注意看这种情况下的其__path__属性):
1 2 3  |  | 
例子5:包内相对导入
文件系统结构:
1 2 3  |  | 
在 p3/do_addition.py 中实现加法:
1 2  |  | 
在 p3/addition.py 中调用它来实现加法接口:
1 2 3 4  |  | 
或者
1 2 3 4  |  | 
在包外的应用程序a5.py中,正常使用它:
1 2 3  |  | 
相对导入只能使用from .xxx import yyy 这种带有from的形式(根据层级可以有2个或3个点),而不能用 import .xxx。特别的,它不能在执行脚本中使用,比如在a5.py中不能使用相对导入,因为执行时其 __name__ 不是一个路径。
相对导入的背景,见 PEP328。这个变化在将python代码从python2移植到到python3还真的头疼。
例子6:模组作为脚本执行
文件系统结构:
1 2  |  | 
其中,p4/m2.py定义一个函数:
1 2  |  | 
在p4/m3.py中使用它:
1 2 3 4  |  | 
此时,由于存在相对导入,如果试图直接运行它:
1 |  | 
将直接遇到异常
1 2 3 4  |  | 
要正常运行它,需要作为模块运行:
1 |  | 
当然,此时,其__name__也不再是 __main__,输出结果:
1 2  |  | 
如果将 p4/m3.py 重命名为 p4/__main__.py,执行会更简单:
1 |  | 
模块(module)与 package(包)概念
import 用于导入一个 module,多个 module 组织成 package。
模块(module)
关于 Module,手册中如是说:
An object that serves as an organizational unit of Python code. Modules have a namespace containing arbitrary Python objects. Modules are loaded into Python by the process of importing.
- 是Pyton代码的组织单元
 - 有一个可包含任何Python对象的命名空间
 - 通过import机制导入
 
Module的常见形式:
- debao.py
 - debao.pyc
 - debao.pyd【Windows only】
 - debao.so 【unix/linux/MacOS】
 debao/__init__.py- debao.zip
 
曾经有过 debao.pyo 这种带优化的pyc文件,现在已经统一到 .pyc文件中。
按位置,大致可以分为:
- 内置库(Builtin Libraries):编译进解释器
 - 标准库(Standard Libraries):位于Lib
 - Site库(Site-specific Libraries):位于Lib/site-packages
 - 其他:PYTHONPATH 或 sys.path 路径下
 - 用户自定义模块
 
包(package)
关于 Package,手册中如是说:
A Python module which can contain submodules or recursively, subpackages. Technically, a package is a Python module with a path attribute.
- 是一个Module(一个包含 
__path__属性的Module) - 包含其他子Module和子Package
 
Package 又分为:
- 常规包(regular package):也叫传统包(traditional package),比如,包含
__init__.py文件的文件夹就是一个常规包。【Python1.5 正式引入Pakcage】 - 命名空间包(namespace package):作为子package的容器存在,不包含
__init__.py文件。详见PEP420。【Python3.3 正式引入命名空间Package】 
模块文件名称.pyc、.pyd(.so)
.pyc 缓存
.pyc 文件是 Python 编译后的字节码文件,它包含了 Python 解释器可以直接执行的字节码。在 Python 程序首次导入一个模块时,Python 会自动将该模块编译成 .pyc 文件,并将其存储在一个特定的目录下(如 __pycache__ 目录)
比如:在前面的例子中,会生成如下缓存文件:
1 2  |  | 
而在python早期时,.pyc 都是都是和 .py 文件肩并肩的,且没有.cpython-312这样的东西存在。
在 PEP3147: PYC Repository Directories中,详细描述了.pyc 文件为什么放置到__历史。
格式
1 |  | 
有助于不同版本python的缓存信息可以共存。
.pyd/.so 文件
在Windows下,一个pyd模块,完整的文件名到底怎么命名?
debao.pyddebao.abi3.pyddebao.cp312-win_amd64.pyd
后缀到底应该是什么?参考PEP0720通过EXTENSION_SUFFIXES可以看出端倪:
1 2 3  |  | 
Linux下结果:
['.cpython-312-x86_64-linux-gnu.so', '.abi3.so', '.so']
更完整的一些列表?
1 2  |  | 
Linux下结果:
['.py', '.pyc', '.cpython-312-x86_64-linux-gnu.so', '.abi3.so', '.so']
abi3
比较奇怪,Linux下面出现 abi3 字样,而Windows下面却没有。这个和PythonC API 稳定性 相关:
- Stable API
 - UnStable API:从Python3.12到Python3.12可能会变化
 
对稳定API,在Windows下使用时,需要连接到 Python3.dll 而不是 Python312.dll。
参考
- https://docs.python.org/3/reference/import.html
 - https://docs.python.org/3/library/importlib.html
 - https://docs.python.org/3/tutorial/modules.html
 - https://docs.python.org/3/library/zipimport.html
 - https://peps.python.org/pep-0328/
 - https://peps.python.org/pep-0420/
 - https://peps.python.org/pep-0402/
 - https://docs.python.org/3/using/windows.html#windows-finding-modules
 - https://ref.readthedocs.io/en/latest/understanding_python/imports/
 - https://realpython.com/python-import/