Python 导入系统
import 由 finders/loaders 完成定位与执行,结果缓存于 sys.modules;可扩展但应谨慎。
#concept
#growing
#dev
#python
#python
[!info] related notes
Python 导入系统
这个概念为什么出现
插件系统、命名空间包、延迟加载都碰到导入钩子。理解 finders/loaders 与缓存,才能诊断“导入到错误文件/循环/未刷新”。
[!abstract] 一句话理解 导入先查 sys.modules;未命中时通常通过 finder/loader 创建模块,先放入缓存再执行代码,以支持循环导入。自定义 hook 可扩展,但增加调试成本。
最小可观察示例
场景:查看已缓存模块与查找路径
# import_system_observe.py
# 业务意图:观察 sys.modules 与 sys.path 入口。
# 教学点:缓存与搜索路径。
import sys
import json
def main() -> None:
print("json cached?", "json" in sys.modules)
print("path0:", sys.path[0])
print("json file:", getattr(json, "__file__", None))
if __name__ == "__main__":
main()
建议运行:
python import_system_observe.py
期望输出:json cached? True 与路径信息。
结合场景再看三个关注点
- 标准库导入后即缓存。
- 此脚本模式下 sys.path[0] 通常是脚本目录;-m、-c、交互模式及 -P/-I 等安全选项会改变初始搜索路径。
- 改 path 是权宜之计。
核心概念与准确模型
- sys.modules 按模块全名缓存;未命中才进入 finder 查找。
- finder 返回 module spec,loader 创建模块(或用默认创建)。
- 模块先写入 sys.modules,再由 exec_module 执行;执行失败会移除失败模块,已成功加载的依赖通常仍留在缓存。
- 子模块搜索使用父包 path;namespace package 不要求 init.py。
边界情况与反直觉行为
- 删除 sys.modules 再导入可产生另一个模块对象,旧引用不会自动更新;reload 也不会更新外部 from-import 已绑定的名字。
- 循环导入 半初始化
- zipimport 等特殊 finder
常见误区
[!warning] 常见误区:import 只是文本包含 错误理解:C 的 #include。
正确模型:执行并缓存模块对象。
工程实践
- 应用用包导入
- 插件入口用明确注册
- 少写自定义 import hook
本节总结
导入系统是可扩展运行时子系统。日常够用“包+绝对导入”,进阶才碰 hook。
自测题
sys.modules作用?- 循环导入常见修复?
参考答案
- 缓存模块对象;普通重复导入复用它。reload、删缓存和用另一模块名加载不属于这个保证。
- 重划依赖、延迟导入、把共享类型下沉。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| The import system | 规范 | 全文 |
| importlib | 文档 | API |