IPythonを使用してPython=)で大規模なプロジェクト(複数のファイルとフォルダーに分割)を開発していると、インポートされたモジュールがキャッシュされるという問題が発生します。
問題は、その指示import module
モジュールが変更された場合でも、モジュールを1回だけ読み取ります。そのため、パッケージ内の何かを変更するたびに、IPythonを終了して再起動する必要があります。痛い。
一部のモジュールを適切に強制的にリロードする方法はありますか?または、何らかの方法でPythonがそれらをキャッシュしないようにするには?
いくつかの方法を試しましたが、どれもうまくいきません。特に、いくつかのモジュールや変数がNone
に不思議に等しくなるなど、本当に奇妙なバグに遭遇します。
私が見つけた唯一の賢明なリソースは、pyunitの Reloading Python modules ですが、私はそれを確認していません。そのようなものが欲しいのです。
代わりにIPythonを再起動するか、Pythonインタープリターを再起動してください。
したがって、Pythonで開発する場合、この問題の解決策は何ですか?
編集
明確にするために、明らかに、モジュールの以前の状態に依存するいくつかの古い変数が残っている可能性があることを理解しています。私はそれでいい。なぜPythonでは、あらゆる種類の奇妙なエラーが発生することなくモジュールを強制的にリロードするのが難しいのでしょうか?
具体的には、モジュール全体がoneファイルmodule.py
その後、次のように動作します:
import sys
try:
del sys.modules['module']
except AttributeError:
pass
import module
obj = module.my_class()
このコードは美しく機能し、IPythonを数か月間終了することなく開発できます。
ただし、、私のモジュールが複数のサブモジュールで構成されている場合はいつでも、地獄が崩れます:
import os
for mod in ['module.submod1', 'module.submod2']:
try:
del sys.module[mod]
except AttributeError:
pass
# sometimes this works, sometimes not. WHY?
モジュールが1つの大きなファイルにあるのか、複数のサブモジュールにあるのか、Pythonの場合はどうしてそんなに違うのですか。
インタプリタを終了して再起動することが最善の解決策です。存在しないモジュールからのオブジェクトが存在する可能性があり、モジュールが状態を保存することがあるため、またユースケースが本当にホットリロードを許可する場合でも、考えるのが複雑すぎるため、あらゆる種類のライブリロードまたはキャッシュなしの戦略はシームレスに機能しませんそれだけの価値がある。
import
は、モジュールがsys.modules
にあるかどうかを確認し、ある場合はそれを返します。インポートによってディスクからモジュールを新たにロードする場合、最初にsys.modules
の適切なキーを削除できます。
reload
組み込み関数があります。これは、モジュールオブジェクトを指定すると、ディスクからそれをリロードし、sys.modules
に配置されます。 Edit-実際には、ディスク上のファイルからコードを再コンパイルし、既存のモジュールの__dict__
で再評価します。新しいモジュールオブジェクトを作成する場合とは大きく異なる可能性があります。
マイク・グラハムは正しい。不要になったモジュールのコンテンツを参照するライブオブジェクトがいくつかある場合でも、適切にリロードするのは困難です。既存のオブジェクトは、インスタンス化されたクラスを引き続き参照しますが、from module import symbol
によって作成されたすべての参照は、モジュールの古いバージョンのオブジェクトを指します。多くの微妙に間違ったことが可能です。
編集:通訳を再起動することが最も信頼できるものであるというコンセンサスに同意します。しかし、デバッグのために、次のようなものを試すことができると思います。これが機能しないコーナーケースがあることは確かですが、yourパッケージ、それは役に立つかもしれません。
def reload_package(root_module):
package_name = root_module.__name__
# get a reference to each loaded module
loaded_package_modules = dict([
(key, value) for key, value in sys.modules.items()
if key.startswith(package_name) and isinstance(value, types.ModuleType)])
# delete references to these loaded modules from sys.modules
for key in loaded_package_modules:
del sys.modules[key]
# load each of the modules again;
# make old modules share state with new modules
for key in loaded_package_modules:
print 'loading %s' % key
newmodule = __import__(key)
oldmodule = loaded_package_modules[key]
oldmodule.__dict__.clear()
oldmodule.__dict__.update(newmodule.__dict__)
私はとても簡単に次のようにテストしました:
import email, email.mime, email.mime.application
reload_package(email)
印刷:
reloading email.iterators
reloading email.mime
reloading email.quoprimime
reloading email.encoders
reloading email.errors
reloading email
reloading email.charset
reloading email.mime.application
reloading email._parseaddr
reloading email.utils
reloading email.mime.base
reloading email.message
reloading email.mime.nonmultipart
reloading email.base64mime
IPythonには、各関数呼び出しの前にインポートを自動的に繰り返す autoreload extension が付属しています。少なくとも単純なケースでは機能しますが、あまり頼りにしないでください:私の経験では、特に間接的にインポートされたコードでのみコードの変更が発生する場合、インタープリターの再起動が必要になることがあります。
リンクされたページの使用例:
In [1]: %load_ext autoreload
In [2]: %autoreload 2
In [3]: from foo import some_function
In [4]: some_function()
Out[4]: 42
In [5]: # open foo.py in an editor and change some_function to return 43
In [6]: some_function()
Out[6]: 43
ここには本当に良い答えがいくつかありますが、dreloadについて知っておく価値があります。dreloadは、「ディープリロード」としてIPythonで利用できる機能です。ドキュメントから:
IPython.lib.deepreloadモジュールを使用すると、モジュールを再帰的に再ロードできます。依存関係に加えられた変更は、終了することなく再ロードされます。使用を開始するには、次を実行します。
http://ipython.org/ipython-doc/dev/interactive/reference.html#dreload
IPythonノートブックの「グローバル」として利用できます(少なくとも私のバージョンではv2.0を実行しています)。
HTH
プロジェクトでPythonNetを使用しています。幸いなことに、この問題を完全に解決できるコマンドがあることがわかりました。
using (Py.GIL())
{
dynamic mod = Py.Import(this.moduleName);
if (mod == null)
throw new Exception( string.Format("Cannot find module {0}. Python script may not be complied successfully or module name is illegal.", this.moduleName));
// This command works perfect for me!
PythonEngine.ReloadModule(mod);
dynamic instance = mod.ClassName();
PEP 302 で説明されているインポートフック機構を使用して、モジュール自体ではなく、基になるモジュールオブジェクトで必要な処理を実行できるプロキシオブジェクトを読み込むことができます。再読み込み、参照の削除など。
追加の利点は、現在の既存のコードを変更する必要がなく、この追加モジュール機能がコードの単一ポイントから実際にFinderをsys.meta_path
に追加することで切り離せることです。
実装に関するいくつかの考え:ビルトイン(ビルトインモジュールとは関係ありません)を除くすべてのモジュールを見つけることに同意するFinderを作成し、実際のモジュールオブジェクトの代わりにtypes.ModuleType
からサブクラス化されたプロキシオブジェクトを返すローダーを作成します。ローダーオブジェクトは、ロードされたモジュールへの明示的な参照をsys.modules
に作成することを強制されないことに注意してください。ただし、既におわかりのように、予期せず失敗する可能性があるため、強くお勧めします。プロキシオブジェクトは、すべての__getattr__
、__setattr__
、および__delattr__
をすべてキャッチし、参照している基礎となる実際のモジュールに転送する必要があります。プロキシメソッドで実際のモジュールの内容を非表示にしないため、おそらく__getattribute__
を定義する必要はありません。したがって、何らかの方法でプロキシと通信する必要があります。基礎となる参照を削除し、モジュールをインポートし、返されたプロキシから参照を抽出し、プロキシを削除し、リロードされたモジュールへの参照を保持する特別なメソッドを作成できますちょっと怖いですが、毎回Pythonをリロードせずに問題を修正する必要があります。
import importlib
importlib.reload(<package_name>)
from <package_name> import <method_name>
詳細については、以下を参照してください documentation .
終了して再起動せずに簡単に解決するには、impからのリロードを使用します
import moduleA, moduleB
from imp import reload
reload (moduleB)