モジュールのリロードに関するこの質問 に続き、変更されたモジュールから特定の関数をリロードするにはどうすればよいですか?
擬似コード:
from foo import bar
if foo.py has changed:
reload bar
あなたが望むものは可能ですが、2つのものをリロードする必要があります...最初にreload(foo)
が必要ですが、次にreload(baz)
(baz
がモジュールの名前であると仮定) _from foo import bar
_ステートメントを含む)。
理由について... foo
が最初にロードされると、foo
オブジェクトを含むbar
オブジェクトが作成されます。 bar
をbaz
モジュールにインポートすると、bar
への参照が格納されます。 reload(foo)
が呼び出されると、foo
オブジェクトが空白になり、モジュールが再実行されます。つまり、すべてのfoo
参照は引き続き有効ですが、新しいbar
オブジェクトが作成されています...したがって、どこかにインポートされたすべての参照は、引き続きoldへの参照です= bar
オブジェクト。 baz
をリロードすると、新しいbar
が再インポートされます。
または、モジュールで_import foo
_を実行し、常にfoo.bar()
を呼び出すこともできます。そうすれば、reload(foo)
を実行するたびに、最新のbar
参照を取得できます。
注:Python 3の時点で、再ロード関数は_from importlib import reload
_を介して最初にインポートする必要があります
今日、これを行う適切な方法は次のとおりです。
import sys, importlib
importlib.reload(sys.modules['foo'])
from foo import bar
python 2.7、3.5、3.6でテスト済み。
ホットリロードは、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 #
>>>
モジュールからメソッドをリロードすることはできませんが、モジュールを新しい名前で再度ロードできます。たとえば、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
が変更されている場合は、疑似コードでリロードします。このアプローチは、バーが別のモジュールに存在するOPの場合ではなく、コードをホットリロードしたいコードと同じモジュールに存在する場合に特に適しています。
関数だけを再ロードすることはできないため、モジュールを再ロードするにはreload
を使用する必要があります。
>>> import sys
>>> reload(sys)
<module 'sys' (built-in)>
>>>