4

(質問に直接ジャンプして、さらに下に移動し、紹介をスキップすることができます。)

ユーザー定義クラスからPythonオブジェクトを選択することには、一般的な問題があります。

# This is program dumper.py
import pickle

class C(object):
    pass

with open('obj.pickle', 'wb') as f:
    pickle.dump(C(), f)

実際、別のプログラムからオブジェクトを取り戻そうとするとloader.py

# This is program loader.py
with open('obj.pickle', 'rb') as f:
    obj = pickle.load(f)

結果は

AttributeError: 'module' object has no attribute 'C'

実際、クラスは名前( "C")でピクルスにされており、loader.pyプログラムは。について何も知りませんC。一般的な解決策は、

from dumper import C  # Objects of class C can be imported

with open('obj.pickle', 'rb') as f:
    obj = pickle.load(f)

ただし、このソリューションには、ピクルス化されたオブジェクトによって参照されるすべてのクラスをインポートする必要があるという事実を含む、いくつかの欠点があります(多くの場合があります)。さらに、ローカル名前空間はdumper.pyプログラムからの名前によって汚染されます。

現在、これに対する解決策は、ピクルスにする前に完全に修飾されたオブジェクトで構成されています。

# New dumper.py program:
import pickle
import dumper  # This is this very program!

class C(object):
    pass

with open('obj.pickle', 'wb') as f:
    pickle.dump(dumper.C(), f)  # Fully qualified class

上記の元のloader.pyプログラムでの選択解除が直接機能するようになりました(実行する必要はありませんfrom dumper import C)。

質問:さて、からの他のクラスはdumper.py、ピクルスにすると自動的に完全に修飾されるようです。これがどのように機能するか、そしてこれが信頼できる文書化された動作であるかどうかを知りたいです。

import pickle
import dumper  # This is this very program!

class D(object):  # New class!
    pass

class C(object):
    def __init__(self):
        self.d = D()  # *NOT* fully qualified

with open('obj.pickle', 'wb') as f:
    pickle.dump(dumper.C(), f)  # Fully qualified pickle class

現在、元のloader.pyプログラムでの選択解除も機能します(行う必要はありませんfrom dumper import C)。完全に資格print obj.dのあるクラスを提供しますが、これは驚くべきことです。

<dumper.D object at 0x122e130>

この動作は非常に便利です。これは、一番上のピクルスオブジェクトのみがモジュール名(dumper.C())で完全に修飾されている必要があるためです。しかし、この動作は信頼でき、文書化されていますか?クラスが名前( "D")でピクルスにされているのに、ピクルス解除されたself.d属性がクラスdumper.D(一部のローカル Dクラスではない)であると判断されるのはなぜですか?

PS:質問、洗練された:この質問への答えを示すかもしれないいくつかの興味深い詳細に気づきました:

ピクルスプログラムdumper.pyでは、最初のプログラム(なしのプログラム)で、をprint self.d印刷します。一方、inを使用してオブジェクトを実行および作成すると、 printが作成されます。属性はPythonによって自動的に修飾されます。したがって、モジュールは、上記の優れたピッキング解除動作には何の役割も果たさないようです。<__main__.D object at 0x2af450>dumper.pyimport dumperimport dumperdumper.C()dumper.pyprint self.d<dumper.D object at 0x2af450>self.dpickle

したがって、問題は本当にです。2番目のケースでは、なぜPythonD()が完全修飾に変換されるのでしょうか。dumper.Dこれはどこかに文書化されていますか?

4

2 に答える 2

3

クラスがメインモジュールで定義されている場合、pickleは、それらが選択解除されたときにそれらを見つけることを期待します。最初のケースでは、クラスはメインモジュールで定義されていたため、ローダーが実行されると、ローダーがメインモジュールになり、pickleはクラスを見つけることができません。の内容を見ると、CクラスとDクラスの名前空間としてエクスポートされた名前がobj.pickle表示されます。__main__

2番目のケースでは、dumper.pyはそれ自体をインポートします。これで、実際には2つの別々のCクラスとDクラスのセットが定義されました。1つは__main__名前空間に、もう1つは名前空間に設定されていdumperます。dumper名前空間内の1つをシリアル化します(obj.pickle確認するために調べてください)。

pickleは、名前空間が見つからない場合、動的にインポートしようとします。そのため、loader.pyを実行すると、 pickle自体がdumper.pyとdumper.Cおよびdumper.Dクラスをインポートします。

dumper.pyとloader.pyの2つの別個のスクリプトがあるため、共通のインポートモジュールでそれらが共有するクラスを定義することだけが意味があります。

common.py

class D(object):
    pass

class C(object):
    def __init__(self):
        self.d = D()

loader.py

import pickle

with open('obj.pickle','rb') as f:
    obj = pickle.load(f)

print obj

dumper.py

import pickle
from common import C

with open('obj.pickle','wb') as f:
    pickle.dump(C(),f)

C()この場合、dumper.pyダンプであっても、 pickleはそれがcommon.Cオブジェクトであることを認識していることに注意してください(を参照obj.pickle)。loader.pyを実行すると、common.pyが動的にインポートされ、オブジェクトの読み込みに成功します。

于 2011-05-15T16:53:56.600 に答える
2

何が起こるかを次に示します。内部からインポートdumper(または実行from dumper import C)するとdumper.pyプログラム全体が再度解析されます(これは、モジュールに印刷を挿入することで確認できます)。dumperはすでにロードされているモジュールではないため(__main__ただし、ロードされていると見なされます)、この動作は予想されます。はにありませんsys.modules

Markの回答に示されているように、モジュールをインポートすると、モジュールで定義されたすべての名前が自然に修飾されるため、ファイルを再評価するときself.d = D()にクラスであると解釈されます(これは、Markの回答の解析と同等です)。dumper.Ddumper.pycommon.py

このように、import dumper(または)トリックが説明され、ピクルスはクラスだけでなくクラスもfrom dumper import C完全に修飾します。これにより、外部プログラムによる選択が簡単になります。CD

import dumperこれは、Pythonインタープリターがプログラムを2回解析することを強制することも示していdumper.pyます。これは、効率的でもエレガントでもありません。したがって、プログラムでクラスをピクルスにし、別のプログラムでそれらをアンピクルすることは、マークの回答で概説されているアプローチを介して行うのがおそらく最善です。ピクルスされたクラスは別のモジュールにある必要があります。

于 2011-05-15T19:31:14.430 に答える