Mar*_*son 759 python module reload python-import
我有一个长期运行的Python服务器,并希望能够在不重新启动服务器的情况下升级服务.这样做的最佳方法是什么?
if foo.py has changed:
unimport foo <-- How do I do this?
import foo
myfoo = foo.Foo()
Run Code Online (Sandbox Code Playgroud)
cdl*_*ary 734
您可以使用reload内置函数在已导入模块时重新加载模块:
from importlib import reload # Python 3.4+ only.
import foo
while True:
# Do some things.
if is_changed(foo):
foo = reload(foo)
Run Code Online (Sandbox Code Playgroud)
在Python 3中,reload被移动到imp模块.在3.4中,imp被弃用赞成importlib,reload并被添加到后者中.当定位3或更高版本时,要么在调用时引用相应的模块,要么reload导入它.
我认为这就是你想要的.像Django的开发服务器这样的Web服务器使用它,这样您就可以看到代码更改的效果,而无需重新启动服务器进程本身.
引用文档:
重新编译Python模块的代码并重新执行模块级代码,定义一组新的对象,这些对象绑定到模块字典中的名称.扩展模块的init功能不是第二次调用.与Python中的所有其他对象一样,只有在引用计数降为零后才会回收旧对象.模块名称空间中的名称将更新为指向任何新对象或已更改的对象.对旧对象的其他引用(例如模块外部的名称)不会反弹以引用新对象,如果需要,必须在每个命名空间中进行更新.
正如您在问题中提到的,Foo如果Foo类位于foo模块中,您将不得不重建对象.
Pau*_*ite 245
在Python 3.0-3.3中,您将使用: imp.reload(module)
但是,imp在3.4中被弃用,赞成importlib(感谢@Stefan!).
因此,我认为你现在使用了importlib.reload(module),虽然我不确定.
Gre*_*ind 88
如果模块不是纯Python,则删除模块尤其困难.
以下是一些信息:如何真正删除导入的模块?
您可以使用sys.getrefcount()来查找实际的引用数.
>>> import sys, empty, os
>>> sys.getrefcount(sys)
9
>>> sys.getrefcount(os)
6
>>> sys.getrefcount(empty)
3
Run Code Online (Sandbox Code Playgroud)
大于3的数字表示很难摆脱该模块.本土的"空"(不含任何)模块应该在之后进行垃圾收集
>>> del sys.modules["empty"]
>>> del empty
Run Code Online (Sandbox Code Playgroud)
因为第三个引用是getrefcount()函数的工件.
bob*_*nce 67
reload(module),但只有它完全是独立的.如果其他任何东西都有对模块(或属于该模块的任何对象)的引用,那么你会得到由旧代码挂起比你预期的更长时间引起的细微和好奇的错误,以及isinstance不能在不同版本的相同的代码.
如果您有单向依赖项,则还必须重新加载依赖于重新加载的模块的所有模块,以去除对旧代码的所有引用.然后递归地重新加载依赖于重新加载的模块的模块.
如果您具有循环依赖关系(例如,在处理重新加载包时非常常见),则必须一次性卸载组中的所有模块.您无法执行此操作,reload()因为它将在刷新依赖项之前重新导入每个模块,从而允许旧引用进入新模块.
在这种情况下,唯一的方法是破解sys.modules,这是一种不受支持的.您必须通过并删除sys.modules下次导入时要重新加载的每个条目,并删除其值为None处理缓存失败的相对导入的实现问题的条目.它并不是非常好,但只要你有一套完全自包含的依赖项,不会将引用留在代码库之外,它就可以使用了.
最好重启服务器.:-)
小智 61
if 'myModule' in sys.modules:
del sys.modules["myModule"]
Run Code Online (Sandbox Code Playgroud)
goe*_*tzc 56
对于Python 2,使用内置函数reload():
reload(module)
Run Code Online (Sandbox Code Playgroud)
对于Python 2和3.2-3.3,使用从模块imp重新加载:
import imp
imp.reload(module)
Run Code Online (Sandbox Code Playgroud)
但imp 不推荐使用,因为3.4版本支持导入库中,所以使用:
import importlib
importlib.reload(module)
Run Code Online (Sandbox Code Playgroud)
要么
from importlib import reload
reload(module)
Run Code Online (Sandbox Code Playgroud)
Mat*_*son 21
以下代码允许您兼容Python 2/3:
try:
reload
except NameError:
# Python 3
from imp import reload
Run Code Online (Sandbox Code Playgroud)
您可以reload()在两个版本中使用它,这使事情变得更简单.
Jos*_*vin 17
接受的答案不处理来自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")
Run Code Online (Sandbox Code Playgroud)
在重新加载的情况下,我们将顶级名称重新分配给新重新加载的模块中存储的值,这些值会更新它们.
Ric*_*all 15
这是重新加载模块的现代方法:
from importlib import reload
Run Code Online (Sandbox Code Playgroud)
只需键入reload(MODULE),替换MODULE为要重新加载的模块的名称.
例如,reload(math)将重新加载数学函数.
nev*_*ves 13
如果您不在服务器中,但正在开发并需要经常重新加载模块,这里有一个很好的提示.
首先,确保您使用的是Jupyter Notebook项目中出色的IPython shell.安装Jupyter后,您可以使用ipython,或者jupyter console甚至更好地启动它jupyter qtconsole,这将为您提供一个漂亮的彩色控制台,在任何操作系统中都可以完成代码.
现在在shell中,键入:
%load_ext autoreload
%autoreload 2
Run Code Online (Sandbox Code Playgroud)
现在,每次运行脚本时,都会重新加载模块.
除此之外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.
Run Code Online (Sandbox Code Playgroud)
小智 7
对于那些想要卸载所有模块的人(当在Emacs下的Python解释器中运行时):
for mod in sys.modules.values():
reload(mod)
Run Code Online (Sandbox Code Playgroud)
更多信息在重新加载Python模块中.
之前的解决方案仅适用于获取重置信息,但它不会更改所有引用(多于reload但少于所需的引用)。为了实际设置所有引用,我必须进入垃圾收集器,并在那里重写引用。现在它就像一个魅力!
请注意,如果 GC 关闭,或者重新加载不受 GC 监控的数据,则此操作将不起作用。如果你不想搞乱GC,原来的答案可能对你来说就足够了。
新代码:
import importlib
import inspect
import gc
from enum import EnumMeta
from weakref import ref
_readonly_attrs = {'__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__',
'__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__func__', '__ge__', '__get__',
'__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__',
'__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__',
'__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', '__weakref__', '__members__', '__mro__', '__itemsize__', '__isabstractmethod__',
'__basicsize__', '__base__'}
def reset_module(module, inner_modules_also=True):
"""
This function is a stronger form of importlib's `reload` function. What it does, is that aside from reloading a
module, it goes to the old instance of the module, and sets all the (not read-only) attributes, functions and classes
to be the reloaded-module's
:param module: The module to reload (module reference, not the name)
:param inner_modules_also: Whether to treat ths module as a package as well, and reload all the modules within it.
"""
# For the case when the module is actually a package
if inner_modules_also:
submods = {submod for _, submod in inspect.getmembers(module)
if (type(submod).__name__ == 'module') and (submod.__package__.startswith(module.__name__))}
for submod in submods:
reset_module(submod, True)
# First, log all the references before reloading (because some references may be changed by the reload operation).
module_tree = _get_tree_references_to_reset_recursively(module, module.__name__)
new_module = importlib.reload(module)
_reset_item_recursively(module, module_tree, new_module)
def _update_referrers(item, new_item):
refs = gc.get_referrers(item)
weak_ref_item = ref(item)
for coll in refs:
if type(coll) == dict:
enumerator = coll.keys()
elif type(coll) == list:
enumerator = range(len(coll))
else:
continue
for key in enumerator:
if weak_ref_item() is None:
# No refs are left in the GC
return
if coll[key] is weak_ref_item():
coll[key] = new_item
def _get_tree_references_to_reset_recursively(item, module_name, grayed_out_item_ids = None):
if grayed_out_item_ids is None:
grayed_out_item_ids = set()
item_tree = dict()
attr_names = set(dir(item)) - _readonly_attrs
for sub_item_name in attr_names:
sub_item = getattr(item, sub_item_name)
item_tree[sub_item_name] = [sub_item, None]
try:
# Will work for classes and functions defined in that module.
mod_name = sub_item.__module__
except AttributeError:
mod_name = None
# If this item was defined within this module, deep-reset
if (mod_name is None) or (mod_name != module_name) or (id(sub_item) in grayed_out_item_ids) \
or isinstance(sub_item, EnumMeta):
continue
grayed_out_item_ids.add(id(sub_item))
item_tree[sub_item_name][1] = \
_get_tree_references_to_reset_recursively(sub_item, module_name, grayed_out_item_ids)
return item_tree
def _reset_item_recursively(item, item_subtree, new_item):
# Set children first so we don't lose the current references.
if item_subtree is not None:
for sub_item_name, (sub_item, sub_item_tree) in item_subtree.items():
try:
new_sub_item = getattr(new_item, sub_item_name)
except AttributeError:
# The item doesn't exist in the reloaded module. Ignore.
continue
try:
# Set the item
_reset_item_recursively(sub_item, sub_item_tree, new_sub_item)
except Exception as ex:
pass
_update_referrers(item, new_item)
Run Code Online (Sandbox Code Playgroud)
正如 @bobince 的答案中所写,如果另一个模块中已经存在对该模块的引用(特别是使用as关键字 like导入import numpy as np),则该实例将不会被覆盖。
当应用需要配置模块的“全新”状态的测试时,这对我来说是一个很大的问题,因此我编写了一个名为的函数,reset_module它使用importlib的reload函数并递归地覆盖所有声明的模块的属性。它已经使用 Python 3.6 版本进行了测试。
import importlib
import inspect
from enum import EnumMeta
_readonly_attrs = {'__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__',
'__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__func__', '__ge__', '__get__',
'__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__',
'__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__',
'__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', '__weakref__', '__members__', '__mro__', '__itemsize__', '__isabstractmethod__',
'__basicsize__', '__base__'}
def reset_module(module, inner_modules_also=True):
"""
This function is a stronger form of importlib's `reload` function. What it does, is that aside from reloading a
module, it goes to the old instance of the module, and sets all the (not read-only) attributes, functions and classes
to be the reloaded-module's
:param module: The module to reload (module reference, not the name)
:param inner_modules_also: Whether to treat ths module as a package as well, and reload all the modules within it.
"""
new_module = importlib.reload(module)
reset_items = set()
# For the case when the module is actually a package
if inner_modules_also:
submods = {submod for _, submod in inspect.getmembers(module)
if (type(submod).__name__ == 'module') and (submod.__package__.startswith(module.__name__))}
for submod in submods:
reset_module(submod, True)
_reset_item_recursively(module, new_module, module.__name__, reset_items)
def _reset_item_recursively(item, new_item, module_name, reset_items=None):
if reset_items is None:
reset_items = set()
attr_names = set(dir(item)) - _readonly_attrs
for sitem_name in attr_names:
sitem = getattr(item, sitem_name)
new_sitem = getattr(new_item, sitem_name)
try:
# Set the item
setattr(item, sitem_name, new_sitem)
try:
# Will work for classes and functions defined in that module.
mod_name = sitem.__module__
except AttributeError:
mod_name = None
# If this item was defined within this module, deep-reset
if (mod_name is None) or (mod_name != module_name) or (id(sitem) in reset_items) \
or isinstance(sitem, EnumMeta): # Deal with enums
continue
reset_items.add(id(sitem))
_reset_item_recursively(sitem, new_sitem, module_name, reset_items)
except Exception as ex:
raise Exception(sitem_name) from ex
Run Code Online (Sandbox Code Playgroud)
注意:谨慎使用!在非外围模块(例如,定义外部使用的类的模块)上使用这些可能会导致 Python 中的内部问题(例如 pickling/un-pickling 问题)。
小智 5
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 默认importlib.reload只会重新导入作为参数传递的库。它不会重新加载您的 lib 导入的库。如果您更改了很多文件并且需要导入一个有些复杂的包,则必须进行深度重新加载。
如果您安装了IPython或Jupyter,您可以使用一个函数来深度重新加载所有库:
from IPython.lib.deepreload import reload as dreload
dreload(foo)
Run Code Online (Sandbox Code Playgroud)
如果您没有 Jupyter,请在 shell 中使用以下命令安装它:
pip3 install jupyter
Run Code Online (Sandbox Code Playgroud)
那些正在使用python 3并从importlib重新加载的人。
如果您遇到问题,例如似乎模块无法重新加载...那是因为它需要一些时间来重新编译pyc(最多60秒)。我编写此提示只是想知道您是否遇到过此类问题。
| 归档时间: |
|
| 查看次数: |
590103 次 |
| 最近记录: |