我有一个长期运行的Python服务器,并希望能够在不重新启动服务器的情况下升级服务.这样做的最佳方法是什么?
if foo.py has changed: unimport foo <-- How do I do this? import foo myfoo = foo.Foo()
cdleary.. 734
您可以使用reload
内置函数在已导入模块时重新加载模块:
from importlib import reload # Python 3.4+ only. import foo while True: # Do some things. if is_changed(foo): foo = reload(foo)
在Python 3中,reload
被移动到imp
模块.在3.4中,imp
被弃用赞成importlib
,reload
并被添加到后者中.当定位3或更高版本时,要么在调用时引用相应的模块,要么reload
导入它.
我认为这就是你想要的.像Django的开发服务器这样的Web服务器使用它,这样您就可以看到代码更改的效果,而无需重新启动服务器进程本身.
引用文档:
重新编译Python模块的代码并重新执行模块级代码,定义一组新的对象,这些对象绑定到模块字典中的名称.扩展模块的init功能不是第二次调用.与Python中的所有其他对象一样,只有在引用计数降为零后才会回收旧对象.模块名称空间中的名称将更新为指向任何新对象或已更改的对象.对旧对象的其他引用(例如模块外部的名称)不会反弹以引用新对象,如果需要,必须在每个命名空间中进行更新.
正如您在问题中提到的,Foo
如果Foo
类位于foo
模块中,您将不得不重建对象.
您可以使用reload
内置函数在已导入模块时重新加载模块:
from importlib import reload # Python 3.4+ only. import foo while True: # Do some things. if is_changed(foo): foo = reload(foo)
在Python 3中,reload
被移动到imp
模块.在3.4中,imp
被弃用赞成importlib
,reload
并被添加到后者中.当定位3或更高版本时,要么在调用时引用相应的模块,要么reload
导入它.
我认为这就是你想要的.像Django的开发服务器这样的Web服务器使用它,这样您就可以看到代码更改的效果,而无需重新启动服务器进程本身.
引用文档:
重新编译Python模块的代码并重新执行模块级代码,定义一组新的对象,这些对象绑定到模块字典中的名称.扩展模块的init功能不是第二次调用.与Python中的所有其他对象一样,只有在引用计数降为零后才会回收旧对象.模块名称空间中的名称将更新为指向任何新对象或已更改的对象.对旧对象的其他引用(例如模块外部的名称)不会反弹以引用新对象,如果需要,必须在每个命名空间中进行更新.
正如您在问题中提到的,Foo
如果Foo
类位于foo
模块中,您将不得不重建对象.
在Python 3.0-3.3中,您将使用: imp.reload(module)
该BDFL已经回答了这个问题.
但是,imp
在3.4中被弃用,赞成importlib
(感谢@Stefan!).
因此,我认为你现在使用了importlib.reload(module)
,虽然我不确定.
如果模块不是纯Python,则删除模块尤其困难.
以下是一些信息:如何真正删除导入的模块?
您可以使用sys.getrefcount()来查找实际的引用数.
>>> import sys, empty, os >>> sys.getrefcount(sys) 9 >>> sys.getrefcount(os) 6 >>> sys.getrefcount(empty) 3
大于3的数字表示很难摆脱该模块.本土的"空"(不含任何)模块应该在之后进行垃圾收集
>>> del sys.modules["empty"] >>> del empty
因为第三个引用是getrefcount()函数的工件.
reload(module)
,但只有它完全是独立的.如果其他任何东西都有对模块(或属于该模块的任何对象)的引用,那么你会得到由旧代码挂起比你预期的更长时间引起的细微和好奇的错误,以及isinstance
不能在不同版本的相同的代码.
如果您有单向依赖项,则还必须重新加载依赖于重新加载的模块的所有模块,以去除对旧代码的所有引用.然后递归地重新加载依赖于重新加载的模块的模块.
如果您具有循环依赖关系(例如,在处理重新加载包时非常常见),则必须一次性卸载组中的所有模块.您无法执行此操作,reload()
因为它将在刷新依赖项之前重新导入每个模块,从而允许旧引用进入新模块.
在这种情况下,唯一的方法是破解sys.modules
,这是一种不受支持的.您必须通过并删除sys.modules
下次导入时要重新加载的每个条目,并删除其值为None
处理缓存失败的相对导入的实现问题的条目.它并不是非常好,但只要你有一套完全自包含的依赖项,不会将引用留在代码库之外,它就可以使用了.
最好重启服务器.:-)
if 'myModule' in sys.modules: del sys.modules["myModule"]
对于Python 2,使用内置函数reload():
reload(module)
对于Python 2和3.2-3.3,使用从模块imp重新加载:
import imp imp.reload(module)
但imp
不推荐使用,因为3.4版本支持导入库中,所以使用:
import importlib importlib.reload(module)
要么
from importlib import reload reload(module)
以下代码允许您兼容Python 2/3:
try: reload except NameError: # Python 3 from imp import reload
您可以reload()
在两个版本中使用它,这使事情变得更简单.
接受的答案不处理来自X导入Y的情况.此代码处理它和标准导入案例:
def importOrReload(module_name, *names): import sys if module_name in sys.modules: reload(sys.modules[module_name]) else: __import__(module_name, fromlist=names) for name in names: globals()[name] = getattr(sys.modules[module_name], name) # use instead of: from dfly_parser import parseMessages importOrReload("dfly_parser", "parseMessages")
在重新加载的情况下,我们将顶级名称重新分配给新重新加载的模块中存储的值,这些值会更新它们.
这是重新加载模块的现代方法:
from importlib import reload
只需键入reload(MODULE)
,替换MODULE
为要重新加载的模块的名称.
例如,reload(math)
将重新加载数学函数.
如果您不在服务器中,但正在开发并需要经常重新加载模块,这里有一个很好的提示.
首先,确保您使用的是Jupyter Notebook项目中出色的IPython shell.安装Jupyter后,您可以使用ipython
,或者jupyter console
甚至更好地启动它jupyter qtconsole
,这将为您提供一个漂亮的彩色控制台,在任何操作系统中都可以完成代码.
现在在shell中,键入:
%load_ext autoreload %autoreload 2
现在,每次运行脚本时,都会重新加载模块.
除此之外2
,还有autoreload魔术的其他选项:
%autoreload Reload all modules (except those excluded by %aimport) automatically now. %autoreload 0 Disable automatic reloading. %autoreload 1 Reload all modules imported with %aimport every time before executing the Python code typed. %autoreload 2 Reload all modules (except those excluded by %aimport) every time before executing the Python code typed.
对于那些想要卸载所有模块的人(当在Emacs下的Python解释器中运行时):
for mod in sys.modules.values(): reload(mod)
更多信息在重新加载Python模块中.
Enthought Traits有一个适用于此的模块.https://traits.readthedocs.org/en/4.3.0/_modules/traits/util/refresh.html
它将重新加载已更改的任何模块,并更新正在使用它的其他模块和实例化对象.它在大多数情况下都不能用于__very_private__
方法,并且可以阻止类继承,但它在编写PyQt guis时需要重新启动宿主应用程序,或者在Maya或Nuke等程序中运行的东西,这使我节省了大量时间.它可能在20-30%的时间内不起作用,但它仍然非常有用.
Enthought的软件包在它们改变的那一刻不会重新加载文件 - 你必须明确地称它为 - 但如果你真的需要它,那就不应该很难实现
那些正在使用python 3并从importlib重新加载的人。
如果您遇到问题,例如似乎模块无法重新加载...那是因为它需要一些时间来重新编译pyc(最多60秒)。我编写此提示只是想知道您是否遇到过此类问题。