跟进this question regarding reloading a module,如何从已更改的模块重新加载特定功能?
伪码:
from foo import bar
if foo.py has changed:
reload bar
答案 0 :(得分:15)
截至今天,正确的做法是:
import sys, importlib
importlib.reload(sys.modules['foo'])
from foo import bar
在python 2.7,3.5,3.6。
上测试答案 1 :(得分:10)
热重新加载不是你可以用Python做的事情,而不会让你头脑发热。如果没有编写特殊的代码方法,你实际上不能支持重新加载,并且尝试编写和维护支持重新加载的代码需要极端的规则,并且太过混乱而不值得付出努力。测试这样的代码也不是一件容易的事。
解决方案是在代码发生变化时完全重启Python进程。可以无缝地执行此操作,但具体取决于您的特定问题域。
答案 2 :(得分:7)
您想要的是什么,但需要重新加载两件事......首先reload(foo)
,但是您还必须reload(baz)
(假设baz
是包含该模块的模块的名称from foo import bar
声明)。
至于为什么......首次加载foo
时,会创建一个包含foo
对象的bar
对象。将bar
导入baz
模块后,它会存储对bar
的引用。调用reload(foo)
时,foo
对象将被清空,模块将重新执行。这意味着所有foo
引用仍然有效,但已创建新的bar
对象...因此,所有已在某处导入的引用仍引用旧 { {1}}对象。通过重新加载bar
,您可以重新导入新的baz
。
或者,您可以在模块中执行bar
,并始终致电import foo
。这样,无论何时foo.bar()
,您都会获得最新的reload(foo)
参考。
答案 3 :(得分:3)
虽然重新加载函数不是reload
函数的一个特性,但它仍然是可能的。我不建议在生产中使用它,但这是它的工作原理:
您要替换的函数是内存中的某个对象,您的代码可能包含许多对它的引用(而不是函数的名称)。但是,当被调用时,该函数实际上没有保存在该对象中,而是保存在另一个对象中,而该对象又由函数对象在其属性__code__
中引用。因此,只要您有对该函数的引用,就可以更新其代码:
模块mymod:
from __future__ import print_function
def foo():
print("foo")
其他模块/ python会话:
>>> import mymod
>>> mymod.foo()
foo
>>> old_foo_ref = mymod.foo
>>> # edit mymod.py to make function "foo" print "bar"
>>> reload(mymod)
<module 'mymod' from 'mymod.py'>
>>> old_foo_ref() # old reference is running old code
foo
>>> mymod.foo() # reloaded module has new code
bar
>>> old_foo_ref.__code__ = mymod.foo.__code__
>>> old_foo_ref() # now the old function object is also running the new code
bar
>>>
现在,如果您没有对旧函数的引用(即传递给另一个函数的lambda
),您可以尝试使用gc
模块来保持它,搜索所有对象的列表:
>>> def _apply(f, x):
... return lambda: f(x)
...
>>> tmp = _apply(lambda x: x+1, 1) # now there is a lambda we don't have a reference to
>>> tmp()
2
>>> import gc
>>> lambdas = [obj for obj in gc.get_objects() if getattr(obj,'__name__','') == '<lambda>'] # get all lambdas
[<function <lambda> at 0x7f315bf02f50>, <function <lambda> at 0x7f315bf12050>]
# i guess the first one is the one i passed in, and the second one is the one returned by _apply
# lets make sure:
>>> lambdas[0].__code__.co_argcount
1 # so this is the "lambda x: ..."
>>> lambdas[1].__code__.co_argcount
0 # and this is the "lambda: ..."
>>> lambdas[0].__code__ = (lambda x: x+2).__code__ # change lambda to return arg + 2
>>> tmp()
3 #
>>>
答案 4 :(得分:2)
您无法从模块重新加载方法,但可以使用新名称重新加载模块,例如foo2
并说bar = foo2.bar
以覆盖当前参考。
请注意,如果bar
对foo
中的其他内容或任何其他副作用有任何依赖性,您将遇到麻烦。因此,虽然它有效,但它只适用于最简单的情况。
答案 5 :(得分:1)
您必须使用reload
重新加载模块,因为您无法仅重新加载该函数:
>>> import sys
>>> reload(sys)
<module 'sys' (built-in)>
>>>
答案 6 :(得分:1)
如果您是foo.py
的作者,可以写下:
with open('bar.py') as f: bar_code=compile(f.read(),'bar.py','exec')
def bar(a,b):
exec(bar_code)
def reload_bar():
global bar_code
with open('bar.py') as f: bar_code=compile(f.read(),'bar.py','exec')
然后,在您的伪代码中,如果bar.py
已更改,则重新加载。当bar与想要热重载它的代码存在于同一模块中时,这种方法特别好,而不是OP存在于不同模块中的情况。
答案 7 :(得分:0)
这也可以。
# Import of my_function and module
import my_utils
from my_utils import my_function
# Test:
my_function()
# For reloading:
from importlib import reload
reload(my_utils)
from my_utils import my_function
# Test again:
my_function()