如何在Python中重新加载模块的函数?
Following up on this question regarding reloading a module, how do I reload a specific function from a changed module?
pseudo-code:
from foo import bar
if foo.py has changed:
reload bar
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
在 Python 中,热重载不是你可以在不爆头的情况下可靠地完成的事情。如果不以特殊方式编写代码,您实际上无法支持重新加载,并且尝试编写和维护支持重新加载的代码需要严格的纪律,并且过于混乱,不值得付出努力。测试这样的代码也不是一件容易的事。
解决方案是当代码发生更改时完全重新启动 Python 进程。可以无缝地完成此操作,但具体方式取决于您的具体问题领域。
Hot reloading is not something you can do in Python reliably without blowing up your head. You literally cannot support reloading without writing code special ways, and trying to write and maintain code that supports reloading with any sanity requires extreme discipline and is too confusing to be worth the effort. Testing such code is no easy task either.
The solution is to completely restart the Python process when code has changed. It is possible to do this seamlessly, but how depends on your specific problem domain.
虽然重新加载函数不是
reload
函数的功能,但它仍然是可能的。我不建议在生产中这样做,但它的工作原理如下:您要替换的函数是内存中某处的对象,并且您的代码可能包含对它的许多引用(而不是对函数名称的引用)。但是该函数在调用时实际执行的操作并不保存在该对象中,而是保存在另一个对象中,而该对象又由该函数对象在其属性
__code__
中引用。因此,只要您有对该函数的引用,您就可以更新其代码:Module mymod:
Other module / python session:
现在,如果您没有对旧函数的引用(即 lambda code> 传递到另一个函数中),您可以尝试使用
gc
模块通过搜索所有对象的列表来获取它:While reloading of functions is not a feature of the
reload
function, it is still possible. I would not recommend doing it in production, but here is how it works:The function you want to replace is a object somewhere in memory, and your code might hold many references to it (and not to the name of the function). But what that function actually does when called is not saved in that object, but in another object that, in turn, is referenced by the function object, in its attribute
__code__
. So as long as you have a reference to the function, you can update its code:Module mymod:
Other module / python session:
Now, in case you do not have a reference to the old function (i.e. a
lambda
passed into another function), you can try to get a hold of it with thegc
module, by searching the list of all objects:这个也行。
This one also works.
您必须使用
reload
来重新加载模块,因为您不能仅重新加载该函数:You will have to use
reload
to reload the module, since you can't reload just the function:您无法从模块重新加载方法,但可以使用新名称再次加载模块,例如
foo2
并说bar = foo2.bar
来覆盖当前引用。请注意,如果
bar
对foo
中的其他内容有任何依赖或任何其他副作用,您将会遇到麻烦。因此,虽然它有效,但它只适用于最简单的情况。You can't reload a method from a module but you can load the module again with a new name, say
foo2
and saybar = foo2.bar
to overwrite the current reference.Note that if
bar
has any dependencies on other things infoo
or any other side effects, you will get into trouble. So while it works, it only works for the most simple cases.如果您是
foo.py
的作者,您可以这样写:然后,在您的伪代码中,如果
bar.py
已更改,则重新加载。当 bar 与想要热重载它的代码位于同一模块中,而不是 OP 位于不同模块中的情况时,这种方法特别好。If you are the author of
foo.py
, you can write:Then, in your pseudocode, reload if
bar.py
has changed. This approach is especially nice when bar lives in the same module as the code that wants to hot-reload it rather than the OP's case where it lives in a different module.截至今天,执行此操作的正确方法是:
在 python 2.7、3.5、3.6 上测试。
As of today, the proper way of doing this is:
Tested on python 2.7, 3.5, 3.6.
你想要的是可能的,但需要重新加载两件事......首先
reload(foo)
,但你还必须reload(baz)
(假设baz
是包含from foo import bar
语句的模块的名称)。至于为什么......第一次加载
foo
时,会创建一个foo
对象,其中包含一个bar
对象。当您将bar
导入baz
模块时,它会存储对bar
的引用。当调用reload(foo)
时,foo
对象被清空,并且模块重新执行。这意味着所有foo
引用仍然有效,但是已经创建了一个新的bar
对象...因此所有已导入某处的引用仍然是对 的引用旧的bar
对象。通过重新加载baz
,您可以使其重新导入新的bar
。或者,您可以在模块中执行
import foo
操作,并始终调用foo.bar()
。这样,每当您reload(foo)
时,您都会获得最新的bar
引用。注意:从 Python 3 开始,需要首先通过
from importlib import reload
导入 reload 函数What you want is possible, but requires reloading two things... first
reload(foo)
, but then you also have toreload(baz)
(assumingbaz
is the name of the module containing thefrom foo import bar
statement).As to why... When
foo
is first loaded, afoo
object is created, containing abar
object. When you importbar
into thebaz
module, it stores a reference tobar
. Whenreload(foo)
is called, thefoo
object is blanked, and the module re-executed. This means allfoo
references are still valid, but a newbar
object has been created... so all references that have been imported somewhere are still references to the oldbar
object. By reloadingbaz
, you cause it to reimport the newbar
.Alternately, you can just do
import foo
in your module, and always callfoo.bar()
. That way whenever youreload(foo)
, you'll get the newestbar
reference.NOTE: As of Python 3, the reload function needs to be imported first, via
from importlib import reload