Python的热更新

标签:
python热更新 |
分类: Python |
脚本语言和编程语言最大的不同,就是脚本语言是解释运行的,而非编译运行的,在运行期间,可以动态调整运行的内容,而这些是通过编译-链接来运行的编程语言是很难达到的,这篇博客就是来探索Python脚本语言进行热更新的。
一、热更新原理
现在很多软件的开发都是模块化的开发,OOP的开发也是基于一个个耦合性较小的类模块的开发,这样以来,在主程序运行的过程中,需要逐步的将当前使用到的模块导入。在编程语言例如C++中,这样的导入模块的操作,由#include的预处理指令来完成这样地方导入,但是在编译链接完成后,引入模块的过程就不可逆了,也就是很难在运行的时候,重新的载入模块。
而对于Python而言,导入模块的语句有两种,一种是import module,一种是from module import name。其中import module,是查找sys.modules中是否已经存在名字为module的对象,如果没有,那么将指定的module模块,放到系统的sys.modules的字典中,进行导入模块的操作;如果有,那么将module模块放到当前命名空间中,接着,此时就可以访问这个模块中的多个属性了(导入的模块也是一个对象,其中的函数和变量都是模块对象的属性);对于from module import name的操作,也是执行import语句,但并不把module放到名字空间中,并在module中查找name属性,放到命名空间中。
既然模块是放在sys.modules字典中的对象,那么对象更新后,更新字典,就可以获得新的模块的使用,也就是更新了对应的模块本身,那么在执行过程中对sys.modules字典进行更改就可以获得热更新的效果。
二、热更新的实现
Python中最常见的两种热更新的方式,如果是热更新A模块,有如下两种方式:
reload(A)
if A in sys.modules:
这两种方式都能实现对于模块A的重新载入。但是重新载入的模块,对于之后使用该模块功能的方法是保证最新的,但是对于更新之前的使用该模块的,比如使用了该模块中的类的,那么依旧保持的是原来的模块内容。
例如模块A代码如下:
class A(object):
如果更新代码中讲初始化的self.value = 1改成self.value = 2,同时修改print中的内容,讲version 1.0改成version 2.0,那么更新后,新建立的A类的对象,输出的内容是get A value in instance is 2 at version 2.0;而对于更新之前使用的A类的对象,输出内容不发生变化。
因而,对于A 类的更新前的对象,需要如何进行更新呢?首先,得确保你知道这个对象的存在,比如这个对象命名为a_instance,那么需要将a_instance的__class__属性指向新的类定义,即在reload之后,对于已知的需要更新的类对象如a_instance,调用如下代码,其中new_class就是新类定义:
a_instance.__class__ = new_class
三、在线热更新的方案
知道python的热更新的原理以及实现了,那么对于传统的CS架构下的应用热更新就只有下列两种情况了:第一种是对于服务端的热更新,第二种是对于客户端的热更新。对于服务器端的热更新,一般需要在命令行中给出指令,接受到指令的情况下,对于模块做重新的加载;对于第二种情况,一般也是指令,只不过这是接收到服务器的指令,然后将新的文件下载到临时目录,进行替换后重新载入。综合上面的两种的要求,那么对于在线热更新,需要完成的功能是,规定更新指令,接收到指令后,拷贝覆盖文件,更新完文件后,进行热更新模块的操作,最后可以验证结果。基于上述的要求,我采用了PyQt4.0进行界面的开发,来做了一个更新的演示,代码如下,其中更新的指令为up,更新指令后要接需要更新的模块名称,同时当前目录下有以“re_更新模块名称”来命名的文件: