python 动态导入模块实现模块热更新的方法
作者:士多碧莉 发布时间:2022-07-06 17:37:56
最近有个部署需求,需要读取py文件格式的配置项,我的实现思路是把配置文件解析到内存中。主要使用两种方法:
importlib.import_module
types.ModuleType
方法1、使用 import_module 动态导包
先来看看import module使用方法。
主要有两个参数:
package:包名
name:模块名
返回 module 对象
现在开始实现动态导包,成功读取到配置项。
import importlib
settings = importlib.import_module("remote_settings")
这样子就能初步实现动态倒入了,但是我有个需求,就是我的系统好些个模块,用FOR循环导包,然后处理业务。然后问题来了,对同一个“包”导入多次,python并不会重新导入,而是返回内存缓存中该模块的地址。
下面验证一下,第一次写入a = 123,第二次写入a = "hello"。
输出结果,两次都是打印旧版本的变量,可见对同一个模块进行多次import_module,并不能实现热更新。
必须要reload,模块才会更新。
输出结果如下,动态reload后,成功获得新版本a的值。
到此基本实现初步热更新需求了,但是还有个问题:
问题一:重新加载的模块不删除旧版本在符号表中的登记项,比如旧版本中存在变量a,新版本中删除了该变量,但是重载不会更新该变化。
def load_module(module_name):
module = importlib.import_module(module_name)
return importlib.reload(module)
def rewrite_file(file_name, content):
with open(file_name, "w+") as f:
f.write(content)
def main():
rewrite_file(file_name, "a=123\nb=456")
c1 = load_module(module_name)
print(hasattr(c1, "a"))
rewrite_file(file_name, "c=100\nd=200")
c1 = load_module(module_name)
print(hasattr(c1, "a"))
我们期望输出 True、False,但是两次都是输出True,也就是说重新加载的模块不会删除最初旧版本模块在符号表中的登记项。
方法2、使用types.ModuleType 创建模块对象
手动创建module对象,而不是使用内存中的module对象。这种方法不需要判断是否需要重载,而且是真正的更新,会删除旧版本模块的登记项。
import types
def import_from_pyfile(filename):
d = types.ModuleType("config") # 创建一个模块对象
d.__file__ = filename
try:
with open(filename, "r") as config_file:
exec(compile(config_file.read(), filename, "exec"), d.__dict__)
except ImportError as e:
print("failt to read config file: {}".format(filename))
raise e
return d
下面验证一下
我们期望的输出依次是True、False,符合需求
因此,这种方法能让我们的模块实现真正的重载。
一些注意事项
无论是方法1还是方法2,都是返回一个module对象,module对象存在一些共性问题。
问题一:重新加载类不影响类的任何已存实例,已存实例将继续使用原来的定义,只有重新加载后创建的新实例使用新定义。
# 原先的 Dog 定义
# class Dog():
# def __init__(self):
# self.name = None
c1 = load_module(module_name)
old_dog = c1.Dog()
# 中间去修改了 Dog 定义
# class Dog():
# def __init__(self):
# self.name = "旺财"
c1 = load_module(module_name)
new_dog = c1.Dog()
print(old_dog.name, new_dog.name)
>>> ouput:
None 旺财
问题二:模块内的引用,不会被reload。比如模块configA中引用了其他模块(configB),当configB发生变化,重新加载configA,并不会对configB进行重载。
预期应该依次输出 configB version1、configBversion2,但是输出了两次configB version1,这说明了模块内的引用,不会被reload,需要手动更新它。
我这实现了一个递归更新方法,不仅对当前模块热更新,还更新里面所有的引用。
def load_module(module):
if isinstance(module, str): # 首次import
module = importlib.import_module(module)
return importlib.reload(module)
def reload_module(module):
load_module(module)
for key, child_module in vars(module).items():
if isinstance(child_module, types.ModuleType):
reload_module(child_module)
效果如下:
def test_reload_module():
configA = "config"
configB = "./configB.py"
configC = "./configC.py"
rewrite_file(configB, "import configC\nname ='configB version1'")
rewrite_file(configC, "name ='configC version1'")
confA = load_module(configA)
print("原始configB.name:", confA.configB.name)
print("原始configC.name:", confA.configB.configC.name)
a = 123
rewrite_file(configB, "import configC\nname ='configB version2'")
rewrite_file(configC, "name ='configC version2'")
confA = load_module(configA)
print("非递归重载configA, configB.name:", confA.configB.name)
print("非递归重载configA, configC.name:", confA.configB.configC.name)
reload_module(confA)
print("递归重载configA, configB.name:", confA.configB.name)
print("递归重载configA, configC.name:", confA.configB.configC.name)
日志如下:
来源:https://blog.csdn.net/weixin_40647516/article/details/126537108


猜你喜欢
- 让我们描绘一下本文的情节:假设您要在本地机器上运行一个进程,而部分程序逻辑却在另一处。让我们特别假设这个程序逻辑会不时更新, 而您运行进程时
- 在项目中安装mockjs在项目目录下执行以下安装命令npm install mockjs --save在Vue项目中使用mockjs的基本流
- 今天遇到一个问题,要保证页面渲染前请求的数据已经得到了由于user是在异步请求之后保存在session中,而在页面渲染时session中还没
- 本文通过实例代码介绍了如何在jscript和vbscript中使用操作FileSystemObject(fso)对象模式来编程.
- 1. 自己写for循环从array里去掉$tmp这个元素的值<?php$tmp = '324';$arr = arra
- try 块允许您测试代码块以查找错误。except 块允许您处理错误。finally 块允许您执行代码,无论 try 和 except 块的
- 前言这段时间一直在研究飞浆平台,最近试了试PaddleSpeech项目,试着对文本语音做处理。整体的效果个人觉着不算特别优越,只能作为简单的
- 不夸张地说,XML正在接管这个世界,正在成为今天一切Web服务和大多数SOA的基础。XML本身并非一种技术,而是程序设计语言,可支持开发者为
- 1、需要将时间字符串转换成datetime类型,语法:data[‘time'] = pd.to_datetime(data[‘tim
- import导入模块import time #导入的时模块中的所有内容print(time.ctime()) #调用模块中的函数
- 简介Python免费调用百度AI实现图片上面的文字识别步骤安装百度AI库!pip install baidu-aip注册百度AI开放平台先注
- python判断字符串的前两个字母是否是”id"你可以使用 Python 的字符串切片来判断一个字符串的前两个
- python正则表达式 匹配反斜杠正则 需要把原始字符串不被转义的条件下传递给正则模块,正则再去转义。 r表示r后面的字符串为原始字符串,防
- Composer的基本使用在项目中使用composer.json在项目中使用composer,你需要有一个composer.json文件,此
- 本文实例讲述了js实现网页标题栏闪烁提示效果的方法。分享给大家供大家参考。具体分析如下:网页标题栏闪烁效果我们在一些聊天工具会常看到,像现在
- 基本属性cv2.imread(文件名,属性) 读入图像属性:指定图像用哪种方式读取文件 cv2.IMREAD_COLOR:读入彩色图像,默认
- 写在前面数据库本质上是一种共享资源,因此在最大程度提供并发访问性能的同时,仍需要确保每个用户能以一致的方式读取和修改数据。锁机制(Locki
- 平衡二叉树:在上一节二叉树的基础上我们实现,如何将生成平衡的二叉树所谓平衡二叉树:我自己定义就是:任何一个节点的左高度和右高度的差的绝对值都
- 前言在学习SQL 2012基础教程过程中会时不时穿插其他内容来进行讲解,相信看过SQL Server 2012 T-SQL基础教程的童鞋知道
- 有过Web经验的人喜欢使用:<meta http-equiv="refresh" content="1;