web-dev-qa-db-ja.com

Pythonがインポートされたモジュールをキャッシュしないようにする

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の場合はどうしてそんなに違うのですか。

47
Olivier Verdier

インタプリタを終了して再起動することが最善の解決策です。存在しないモジュールからのオブジェクトが存在する可能性があり、モジュールが状態を保存することがあるため、またユースケースが本当にホットリロードを許可する場合でも、考えるのが複雑すぎるため、あらゆる種類のライブリロードまたはキャッシュなしの戦略はシームレスに機能しませんそれだけの価値がある。

12
Mike Graham

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
24
Matt Anderson

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

ここには本当に良い答えがいくつかありますが、dreloadについて知っておく価値があります。dreloadは、「ディープリロード」としてIPythonで利用できる機能です。ドキュメントから:

IPython.lib.deepreloadモジュールを使用すると、モジュールを再帰的に再ロードできます。依存関係に加えられた変更は、終了することなく再ロードされます。使用を開始するには、次を実行します。

http://ipython.org/ipython-doc/dev/interactive/reference.html#dreload

IPythonノートブックの「グローバル」として利用できます(少なくとも私のバージョンではv2.0を実行しています)。

HTH

4
pelson

プロジェクトで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();
2
user7175781

PEP 302 で説明されているインポートフック機構を使用して、モジュール自体ではなく、基になるモジュールオブジェクトで必要な処理を実行できるプロキシオブジェクトを読み込むことができます。再読み込み、参照の削除など。

追加の利点は、現在の既存のコードを変更する必要がなく、この追加モジュール機能がコードの単一ポイントから実際にFinderをsys.meta_pathに追加することで切り離せることです。

実装に関するいくつかの考え:ビルトイン(ビルトインモジュールとは関係ありません)を除くすべてのモジュールを見つけることに同意するFinderを作成し、実際のモジュールオブジェクトの代わりにtypes.ModuleTypeからサブクラス化されたプロキシオブジェクトを返すローダーを作成します。ローダーオブジェクトは、ロードされたモジュールへの明示的な参照をsys.modulesに作成することを強制されないことに注意してください。ただし、既におわかりのように、予期せず失敗する可能性があるため、強くお勧めします。プロキシオブジェクトは、すべての__getattr____setattr__、および__delattr__をすべてキャッチし、参照している基礎となる実際のモジュールに転送する必要があります。プロキシメソッドで実際のモジュールの内容を非表示にしないため、おそらく__getattribute__を定義する必要はありません。したがって、何らかの方法でプロキシと通信する必要があります。基礎となる参照を削除し、モジュールをインポートし、返されたプロキシから参照を抽出し、プロキシを削除し、リロードされたモジュールへの参照を保持する特別なメソッドを作成できますちょっと怖いですが、毎回Pythonをリロードせずに問題を修正する必要があります。

2
toriningen

Pythonバージョン3.4以降

import importlib 
importlib.reload(<package_name>) 
from <package_name> import <method_name>

詳細については、以下を参照してください documentation .

0
Mitendra

本番環境での終了と再起動についてもう一度考えてください

終了して再起動せずに簡単に解決するには、impからのリロードを使用します

import moduleA, moduleB
from imp import reload
reload (moduleB)
0
Feras