我如何卸载(重新加载)一个Python模块?
我有一个长期运行的Python服务器,并希望能够升级服务,而无需重新启动服务器。 这样做的最好方法是什么?
if foo.py has changed: unimport foo <-- How do I do this? import foo myfoo = foo.Foo()
您可以使用Python 2中的reload
内build函数reload
已经导入的模块:
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
东西在不同版本的相同的代码。
如果您有单向依赖关系,则还必须重新加载依赖于重新加载的模块的所有模块,以摆脱对旧代码的所有引用。 然后recursion地重新加载依赖于重载模块的模块。
如果您有循环依赖关系(例如在处理重新加载包时非常常见),则必须一次性卸载组中的所有模块。 你不能用reload()
来做到这一点,因为它会重新导入每个模块的依赖关系被刷新之前,允许旧的引用爬到新的模块。
在这种情况下唯一的方法是破解sys.modules
,这是不受支持的。 您必须通过并删除下一次导入时希望重新加载的每个sys.modules
条目,并删除其值为None
条目以处理执行问题,以caching失败的相对导入。 这不是非常好,但只要你有一个完全独立的依赖关系集,不会在代码库之外留下引用,这是可行的。
这可能是最好的重新启动服务器。 🙂
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 开始被赞成使用importlib ,所以使用:
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")
在重新加载的情况下,我们将顶级名称重新分配到新加载的模块中存储的值,并将其更新。
对于那些想卸载所有模块的人(在Emacs下的Python解释器中运行时):
for mod in sys.modules.values(): reload(mod)
更多信息在Reloading Python模块中 。
Enthought Traits有一个相当适合的模块。 https://traits.readthedocs.org/en/4.3.0/_modules/traits/util/refresh.html
它将重新加载已更改的任何模块,并更新正在使用它的其他模块和实例化对象。 它大多数情况下并不适用于__very_private__
方法,并且可以阻塞类inheritance,但是在编写PyQt guis或者在诸如Maya或者Nuke之类的程序中运行的东西时, 。 大概20-30%的时间不工作,但它仍然非常有帮助。
Enthought的软件包在改变的时候不会重新加载文件 – 你必须明确地调用它 – 但是如果你真的需要的话,这不应该很难实现
对于Abaqus来说,这是它的工作方式。 想象一下你的文件是Class_VerticesEdges.py
sys.path.append('D:\...\My Pythons') if 'Class_VerticesEdges' in sys.modules: del sys.modules['Class_VerticesEdges'] print 'old module Class_VerticesEdges deleted' from Class_VerticesEdges import * reload(sys.modules['Class_VerticesEdges'])
我试图在Sublime Text中重新加载一些东西时遇到了很多麻烦,但是最后我可以编写这个实用程序来根据sublime_plugin.py
用于重新加载模块的代码重新加载Sublime Text上的模块。
这下面接受你重新加载模块从他们的名字空间的path,然后在重新加载后,你可以像平常一样导入。
def reload_module(full_module_name): """ Assuming the folder `full_module_name` is a folder inside some folder on the python sys.path, for example, sys.path as `C:/`, and you are inside the folder `C:/Path With Spaces` on the file `C:/Path With Spaces/main.py` and want to re-import some files on the folder `C:/Path With Spaces/tests` @param full_module_name the relative full path to the module file you want to reload from a folder on the python `sys.path` """ import imp import sys import importlib if full_module_name in sys.modules: module_object = sys.modules[full_module_name] module_object = imp.reload( module_object ) else: importlib.import_module( full_module_name ) def run_tests(): print( "\n\n" ) reload_module( "Path With Spaces.tests.semantic_linefeed_unit_tests" ) reload_module( "Path With Spaces.tests.semantic_linefeed_manual_tests" ) from .tests import semantic_linefeed_unit_tests from .tests import semantic_linefeed_manual_tests semantic_linefeed_unit_tests.run_unit_tests() semantic_linefeed_manual_tests.run_manual_tests() if __name__ == "__main__": run_tests()
如果你第一次运行,这应该加载模块,但如果以后你可以再次方法/函数run_tests()
它将重新加载testing文件。 使用Sublime Text( Python 3.3.6
)时,会发生这种情况,因为它的解释器永远不会closures(除非重启Sublime Text,即Python3.3
解释器)。
另一种方式可能是在一个函数中导入模块。 这种方式,当function完成模块获取垃圾回收。