优先用 importlib.import_module(),它行为明确、文档推荐;__import__() 参数反直觉且易错。importlib.import_module() 只接受点号分隔的包路径,不支持 .py 后缀或文件路径。

动态导入模块时,importlib.import_module() 和 __import__() 该怎么选
直接用 importlib.import_module(),别碰 __import__() —— 后者行为隐晦、参数顺序反直觉(比如 fromlist 影响返回值层级),且官方文档明确建议优先使用前者。
常见错误是传入带 .py 后缀的路径或相对路径字符串,比如 importlib.import_module("utils.py") 或 importlib.import_module("./src/utils"),这会触发 ModuleNotFoundError。它只接受 Python 包路径语法(类似 import 语句的点号分隔形式)。
-
importlib.import_module("json")✅ 标准库模块 -
importlib.import_module("myproject.utils.helper")✅ 已在sys.path中的包 -
importlib.import_module("utils")❌ 若未在顶层或未正确配置__package__,会报错
从文件路径加载模块要用 importlib.util.spec_from_file_location()
当模块不在 sys.path 里,而是你手头有一个具体路径(比如用户上传的插件脚本 /tmp/plugin_v2.py),就不能用 import_module(),得走 spec + loader 流程。
容易漏掉的关键步骤:调用 spec.loader.exec_module() 之后,模块对象才真正初始化(类定义、全局变量赋值、if __name__ == "__main__" 块才会执行)。只调 importlib.util.module_from_spec() 不执行,模块是空壳。
import importlib.util
<p>spec = importlib.util.spec_from_file_location("plugin", "/tmp/plugin_v2.py")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module) # ← 这行不能省
print(module.some_function())</p>
重复导入同一模块会不会重复执行?如何控制缓存行为
默认会缓存:importlib.import_module() 和完整 spec 流程都会把模块写入 sys.modules。第二次导入相同名字(不是相同路径!)时直接返回缓存对象,不会重执行模块顶层代码。
但注意陷阱:如果你用不同名字导入同一个文件(比如两次调用 spec_from_file_location("a", "x.py") 和 spec_from_file_location("b", "x.py")),sys.modules 会存两个键,导致模块被加载两次、初始化两次 —— 全局变量不共享,类也不是同一个。
- 需要强制重载?先删
sys.modules["模块名"],再重新导入(仅限开发调试,生产慎用) - 想确保单例行为?统一用固定模块名(如基于文件 hash 生成唯一名),或手动检查
sys.modules是否已存在 - 热更新场景下,类继承、函数装饰器等可能因旧引用残留导致行为异常,光 reload 不够
模块加载失败时,ImportError 和 ModuleNotFoundError 的区别要分清
ModuleNotFoundError 是 ImportError 的子类,专指“找不到模块”;而 ImportError 还包括模块找到了但内部 import 失败、语法错误、__init__.py 报错等情况。捕获时建议分开处理:
try:
mod = importlib.import_module("myplugin.core")
except ModuleNotFoundError:
print("模块未安装或路径未加入 sys.path")
except ImportError as e:
print(f"模块加载失败:{e}(可能是依赖缺失或语法错误)")
更隐蔽的问题是:模块文件存在,但其 __init__.py 抛异常,此时报的是 ImportError,不是 ModuleNotFoundError —— 很多人误以为只要文件路径对就一定能导进来。
实际工程中,动态加载常用于插件系统或配置驱动逻辑,最麻烦的往往不是导入本身,而是模块间隐式依赖、C 扩展兼容性、以及多线程下 sys.modules 修改引发的竞争条件。这些没法靠 importlib 自动解决,得靠设计约束兜底。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











