脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服务器之家 - 脚本之家 - Python - python 动态导入模块实现模块热更新的方法

python 动态导入模块实现模块热更新的方法

2022-08-30 14:03士多碧莉 Python

这篇文章主要介绍了python 动态导入模块,实现模块热更新,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

最近有个部署需求,需要读取py文件格式的配置项,我的实现思路是把配置文件解析到内存中。主要使用两种方法:

  • importlib.import_module
  • types.ModuleType

方法1、使用 import_module 动态导包

先来看看import module使用方法。

python 动态导入模块实现模块热更新的方法

  • 主要有两个参数:
    • package:包名
    • name:模块
  • 返回 module 对象

现在开始实现动态导包,成功读取到配置项。

import importlib
settings = importlib.import_module("remote_settings")

这样子就能初步实现动态倒入了,但是我有个需求,就是我的系统好些个模块,用FOR循环导包,然后处理业务。然后问题来了,对同一个“包”导入多次,python并不会重新导入,而是返回内存缓存中该模块的地址。

下面验证一下,第一次写入a = 123,第二次写入a = "hello"。

python 动态导入模块实现模块热更新的方法

输出结果,两次都是打印旧版本的变量,可见对同一个模块进行多次import_module,并不能实现热更新

python 动态导入模块实现模块热更新的方法

必须要reload,模块才会更新。

python 动态导入模块实现模块热更新的方法

输出结果如下,动态reload后,成功获得新版本a的值。

python 动态导入模块实现模块热更新的方法

到此基本实现初步热更新需求了,但是还有个问题:

问题一:重新加载的模块不删除旧版本在符号表中的登记项,比如旧版本中存在变量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
b=456")
    c1 = load_module(module_name)
    print(hasattr(c1, "a"))
    
    rewrite_file(file_name, "c=100
d=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

下面验证一下

python 动态导入模块实现模块热更新的方法

我们期望的输出依次是True、False,符合需求

python 动态导入模块实现模块热更新的方法

因此,这种方法能让我们的模块实现真正的重载。

一些注意事项

无论是方法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进行重载。

 

python 动态导入模块实现模块热更新的方法

预期应该依次输出 configB version1、configBversion2,但是输出了两次configB version1,这说明了模块内的引用,不会被reload,需要手动更新它。

python 动态导入模块实现模块热更新的方法

我这实现了一个递归更新方法,不仅对当前模块热更新,还更新里面所有的引用。

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
name ="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
name ="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)

日志如下:

python 动态导入模块实现模块热更新的方法

到此这篇关于python动态导入模块,实现模块热更新的文章就介绍到这了,更多相关python模块热更新内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文地址:https://blog.csdn.net/weixin_40647516/article/details/126537108

延伸 · 阅读

精彩推荐
  • Python对Python 3.2 迭代器的next函数实例讲解

    对Python 3.2 迭代器的next函数实例讲解

    今天小编就为大家分享一篇对Python 3.2 迭代器的next函数实例讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    银霜覆秋枫7322021-04-09
  • Python图文详解OpenCV中光流以及视频特征点追踪

    图文详解OpenCV中光流以及视频特征点追踪

    光流是空间运动物体在观察成像平面上的像素运动的瞬时速度,是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之...

    程序媛一枚~6472021-12-22
  • PythonPyhton模块和包相关知识总结

    Pyhton模块和包相关知识总结

    文中详细整理了关于Python模块和包的相关知识点,刚入门Python的小伙伴们可以学习一下,有助于加深Python基础的理解.而且有详细说明及代码示例,需要的朋友可...

    ʚVVcatɞ8892021-10-29
  • Pythonpython二分法查找实例代码

    python二分法查找实例代码

    二分算法是一种效率比较高的查找算法,其输入的是一个有序的元素列表,如果查找元素包含在列表中,二分查找返回其位置,否则返回NONE,下面这篇文章主要给...

    栾沫4452022-03-04
  • PythonPython魔法方法功能与用法简介

    Python魔法方法功能与用法简介

    这篇文章主要介绍了Python魔法方法功能与用法,结合具体实例形式分析了Python面向对象程序设计中魔法方法的概念、功能、原理、用法及相关操作注意事项...

    蔷薇Nina5002021-06-11
  • Python如何在python字符串中输入纯粹的{}

    如何在python字符串中输入纯粹的{}

    这篇文章主要介绍了如何在python字符串中输入纯粹的{}以及python字符串连接的三种方法,需要的朋友可以参考下...

    丁壮5052021-03-29
  • PythonPython使用zip合并相邻列表项的方法示例

    Python使用zip合并相邻列表项的方法示例

    这篇文章主要介绍了Python使用zip合并相邻列表项的方法,涉及zip、iter函数合并相邻列表项、切片等相关操作技巧,需要的朋友可以参考下...

    快递小可5332021-01-22
  • Pythonpython判断所输入的任意一个正整数是否为素数的两种方法

    python判断所输入的任意一个正整数是否为素数的两种方法

    今天小编就为大家分享一篇python判断所输入的任意一个正整数是否为素数的两种方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看...

    非断丶丶12722021-07-24