Python通过importlib可以实现在程序运行期间重新加载模块,从而替换已有的函数或类定义,达到代码热更新的效果。这种方式比重启进程更轻量,适合调试工具和常驻服务。

为什么需要代码热更新
在下面这些场景中,热更新能显著提升效率:
- 后端服务不希望因改动一行逻辑就中断连接
- 算法调试时需要反复试验新函数
- 插件系统希望动态加载用户提供的模块
importlib基础用法
importlib.reload可以重新执行已导入模块的代码,使模块中的定义被更新。注意reload接收的是模块对象,不是字符串。
import importlib import my_module # 第一次调用 my_module.hello() # 修改磁盘上my_module.py后 importlib.reload(my_module) my_module.hello()
清理模块缓存实现强制重载
有时候模块被其他包间接引用,单靠reload不够。我们可以从sys.modules中删除记录,再使用importlib.import_module重新导入。
import sys
import importlib
module_name = 'my_module'
if module_name in sys.modules:
del sys.modules[module_name]
new_mod = importlib.import_module(module_name)
new_mod.hello()
根据文件路径动态导入
当模块不在常规包路径下时,可以用importlib.util.spec_from_file_location按文件路径加载。
import importlib.util
def load_from_path(path, name):
spec = importlib.util.spec_from_file_location(name, path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
mod = load_from_path('/tmp/plugin.py', 'plugin')
mod.run()
结合文件监听做自动热更新
我们可以记录文件修改时间,在变化时清理缓存并重新导入,实现简单的热更新循环。
import os
import sys
import importlib
import time
path = 'my_module.py'
name = 'my_module'
last_mtime = 0
while True:
mtime = os.path.getmtime(path)
if mtime != last_mtime:
if name in sys.modules:
del sys.modules[name]
mod = importlib.import_module(name)
mod.hello()
last_mtime = mtime
time.sleep(1)
注意事项
热更新并不会更新已经创建的实例对象,旧对象仍使用旧类。若需全部生效,要重新构造对象或设计可重置的状态机制。另外,在多线程环境中重载模块需做好锁保护,避免执行到半成品代码。
合理使用importlib,可以让Python项目具备灵活的热更新能力,但生产环境应配合健康检查与灰度策略。