[英]How to reload a module's function in Python?
截至今天,正確的做法是:
import sys, importlib
importlib.reload(sys.modules['foo'])
from foo import bar
在 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 開始,需要先導入 reload 函數,通過from importlib import reload
熱重載不是你可以在 Python 中可靠地做的事情而不會炸毀你的頭。 如果不以特殊方式編寫代碼,您實際上無法支持重新加載,並且嘗試編寫和維護支持以任何理智的方式重新加載的代碼需要極端的紀律並且太混亂而值得付出努力。 測試這樣的代碼也不是一件容易的事。
解決方案是在代碼發生更改時完全重新啟動 Python 進程。 可以無縫地做到這一點,但具體如何取決於您的特定問題域。
雖然重新加載功能不是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 #
>>>
您將不得不使用reload
加載模塊,因為您不能只重新加載函數:
>>> import sys
>>> reload(sys)
<module 'sys' (built-in)>
>>>
您無法從模塊重新加載方法,但可以使用新名稱再次加載模塊,例如foo2
並說bar = foo2.bar
以覆蓋當前引用。
請注意,如果bar
對foo
其他東西有任何依賴或任何其他副作用,您就會遇到麻煩。 所以雖然它有效,但它只適用於最簡單的情況。
如果你是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 位於不同模塊中的情況時,這種方法特別好。
這個也有效。
# 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()
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.