如何在Python中重新加载模块的函数?

发布于 2024-12-02 20:47:51 字数 237 浏览 2 评论 0原文

跟进这个有关重新加载模块的问题,如何做我从更改的模块中重新加载特定功能?

伪代码:

from foo import bar

if foo.py has changed:
    reload bar

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

烦人精 2024-12-09 20:47:52

在 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.

疯到世界奔溃 2024-12-09 20:47:52

虽然重新加载函数不是 reload 函数的功能,但它仍然是可能的。我不建议在生产中这样做,但它的工作原理如下:
您要替换的函数是内存中某处的对象,并且您的代码可能包含对它的许多引用(而不是对函数名称的引用)。但是该函数在调用时实际执行的操作并不保存在该对象中,而是保存在另一个对象中,而该对象又由该函数对象在其属性 __code__ 中引用。因此,只要您有对该函数的引用,您就可以更新其代码:

Module mymod:

from __future__ import print_function
def foo():
    print("foo")

Other module / python session:

>>> 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 code> 传递到另一个函数中),您可以尝试使用 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  # 
>>> 

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:

from __future__ import print_function
def foo():
    print("foo")

Other module / python session:

>>> 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
>>> 

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 the gc module, by searching the list of all objects:

>>> 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  # 
>>> 
嗼ふ静 2024-12-09 20:47:52

这个也行。

  • 优点:它支持 from ... 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()

This one also works.

  • Pros: it supports from ... import ... instead of module.my_function, and it does not require to write the module as a string.
  • Cons: it needs the module to be imported.
# 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()
|煩躁 2024-12-09 20:47:52

您必须使用 reload 来重新加载模块,因为您不能仅重新加载该函数:

>>> import sys
>>> reload(sys)
<module 'sys' (built-in)>
>>>

You will have to use reload to reload the module, since you can't reload just the function:

>>> import sys
>>> reload(sys)
<module 'sys' (built-in)>
>>>
将军与妓 2024-12-09 20:47:52

您无法从模块重新加载方法,但可以使用新名称再次加载模块,例如 foo2 并说 bar = foo2.bar 来覆盖当前引用。

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

You can't reload a method from a module but you can load the module again with a new name, say foo2 and say bar = foo2.bar to overwrite the current reference.

Note that if bar has any dependencies on other things in foo or any other side effects, you will get into trouble. So while it works, it only works for the most simple cases.

远山浅 2024-12-09 20:47:52

如果您是 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 位于不同模块中的情况时,这种方法特别好。

If you are the author of foo.py, you can write:

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') 

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.

风透绣罗衣 2024-12-09 20:47:51

截至今天,执行此操作的正确方法是:

import sys, importlib
importlib.reload(sys.modules['foo'])
from foo import bar

在 python 2.7、3.5、3.6 上测试。

As of today, the proper way of doing this is:

import sys, importlib
importlib.reload(sys.modules['foo'])
from foo import bar

Tested on python 2.7, 3.5, 3.6.

不喜欢何必死缠烂打 2024-12-09 20:47:51

你想要的是可能的,但需要重新加载两件事......首先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 to reload(baz) (assuming baz is the name of the module containing the from foo import bar statement).

As to why... When foo is first loaded, a foo object is created, containing a bar object. When you import bar into the baz module, it stores a reference to bar. When reload(foo) is called, the foo object is blanked, and the module re-executed. This means all foo references are still valid, but a new bar object has been created... so all references that have been imported somewhere are still references to the old bar object. By reloading baz, you cause it to reimport the new bar.


Alternately, you can just do import foo in your module, and always call foo.bar(). That way whenever you reload(foo), you'll get the newest bar reference.

NOTE: As of Python 3, the reload function needs to be imported first, via from importlib import reload

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文