在 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.pyd
debao.abi3.pyd
debao.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/