Python import 具有幂等性:同一模块在单进程内多次导入仅执行一次模块体,后续直接复用 sys.modules 中的模块对象;模块级初始化逻辑应置于顶层,延迟初始化可用惰性函数配合标记变量实现;该特性不跨进程,多进程需外部协调机制。

Python 的 import 语句具有天然的“幂等性”:同一模块在同一个 Python 进程中被多次 import,其模块体(module body)仅执行一次,后续导入直接复用已加载的模块对象。这一特性是实现全局单例模块的核心机制,无需额外加锁或判断。
模块级代码只运行一次
模块顶层(即缩进为 0 级)的可执行语句,在首次导入时执行;之后再 import 同名模块,Python 直接从 sys.modules 返回已有模块对象,不再重新执行模块代码。
- 模块初始化逻辑(如创建连接、读取配置、实例化对象)应放在模块顶层,而非函数内
- 避免把单例创建写在函数里并反复调用——那不是模块单例,而是函数级缓存
- 例如:
config.py中写DB = Database.connect(...),所有地方import config都共享同一个config.DB
利用 sys.modules 显式控制加载时机
若需延迟初始化(比如依赖环境变量就绪后再构建单例),可在模块中定义一个惰性初始化函数,并借助模块变量标记是否已完成:
- 在模块内声明
_instance = None和_initialized = False - 提供
get_singleton()函数,内部检查_initialized,未初始化则执行构造逻辑并标记 - 仍由
import触发模块加载,但具体实例化推迟到首次调用get_singleton()
注意跨子进程/线程的边界
import 的幂等性仅作用于单个 Python 解释器进程内:
- 多进程场景下,每个子进程有独立的
sys.modules,模块会各自初始化一次 - 多线程下无影响,因共享同一解释器状态
- 如需跨进程单例(如共享数据库连接池),需改用外部协调机制(Redis、文件锁、multiprocessing.Manager 等)
避免意外打破幂等性的常见错误
以下操作会导致模块被重复加载,破坏单例语义:
- 使用
importlib.reload()—— 强制重执行模块体,覆盖原有模块对象 - 通过不同路径导入同一模块(如
from pkg import mod和import pkg.mod在某些包结构下可能被视为不同模块) - 动态构造模块名并用
__import__()或importlib.import_module()多次导入(若模块名字符串不完全一致) - 确保所有导入都使用统一、规范的模块路径(推荐绝对导入)

















