如何在Python中重新加载模块的功能?

时间:2011-09-01 13:34:04

标签: python methods import reload

跟进this question regarding reloading a module,如何从已更改的模块重新加载特定功能?

伪码:

from foo import bar

if foo.py has changed:
    reload bar

8 个答案:

答案 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以覆盖当前参考。

请注意,如果barfoo中的其他内容或任何其他副作用有任何依赖性,您将遇到麻烦。因此,虽然它有效,但它只适用于最简单的情况。

答案 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 ...而不是module.my_function,并且不需要将模块写为字符串。
  • 缺点:它需要导入模块。
# 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()