8

これまでのところ、私がやったことは次のとおりです。

import pickle

class MyPickler(pickle.Pickler):
    def __init__(self, file, protocol=None):
        super(MyPickler, self).__init__(file, protocol)

class MyUnpickler(pickle.Unpickler):
    def __init__(self, file):
        super(MyUnpickler, self).__init__(file) 

私の主な方法では、これは主に私が持っているものです

#created object, then... 
pickledObject = 'testing.pickle'
with open(pickledObject,'wb') as f:
    pickle = MyPickler(f)
    pickle.dump(object) #object is the object I want to pickle, created before this

with open(pickledObject, 'r') as pickledFile:
    unpickle = MyUnpickler(pickledFile)
    object2 = unpickle.load()

ただし、スーパーメソッドが呼び出されると、次のエラーが発生します。 TypeError: must be type, not classobj

load と dump の 2 つのメソッドだけを上書きするにはどうすればよいでしょうか。pickle ファイルは C:\Python27/lib/pickle.py の下にあります。

編集 enum.py ファイルはここにあります: http://dpaste.com/780897/

オブジェクトの詳細: オブジェクトは次のように初期化されます。

object = CellSizeRelation(CellSizeRelation.Values.FIRST)

そして、CellSizeRelation は Enumeration を使用するクラスです:

class CellSizeRelation(Option):
    Values = enum.Enum('FIRST',
                       'SECOND')

オブジェクトをピクルする前に、次のようにします。

print object.Values._values 
print object.value.enumtype 

出力

[EnumValue(<enum.Enum object at 0x02E80E50>, 0, 'FIRST'), EnumValue(<enum.Enum object at 0x02E80E50>, 1, 'SECOND')
<enum.Enum object at 0x02E80E50>

同じものを unpickle して出力すると、次の出力が得られます。

[EnumValue(<enum.Enum object at 0x02E80E50>, 0, 'FIRST'), EnumValue(<enum.Enum object at 0x02E80E50>, 1, 'SECOND')
<enum.Enum object at 0x02ECF750>

問題は、2 番目のオブジェクト アドレスが変更されることです。初めて初期化したとき、enumtype_valuesは同じアドレスを持ちます。ただし、解凍後はアドレスが変わります。2 つの enumValue を比較しようとすると、コードが壊れます。クラスを見るとenumValue、compare 関数は次のことを試みます。

try:
        assert self.enumtype == other.enumtype
        result = cmp(self.index, other.index)

アドレスが変更されるため、アサート関数は失敗します。unpickle されたときに enumtype のアドレスが変更されないようにする必要があります。私は単純に、ピクルされていないファイルから「FIRST」という値を取得し、そのインデックスを見つけて、次のようにオブジェクトを再初期化することを考えていました。

def load:
    object = CellSizeRelation(CellSizeRelation.Values[INDEX])
    return object
4

2 に答える 2

11

ロードおよびアンロード機能をカスタマイズするのではなく、オブジェクトの状態を pickle 化および unpickle 化する方法をカスタマイズしたい。

通常のクラス インスタンスの Pickling と unpickling の章を学習する必要があります。この場合、 __getstate__andメソッドを定義する__setstate__だけで十分です。

あなたの場合、EnumValue定数であることが意図されているインスタンスを持つクラスレベルの属性があるということです。しかし、ピッキングを解除EnumValueすると、クラスレベルの属性に接続されなくなった新しいインスタンスが作成されます。

EnumValueインスタンスには、状態をのindexインスタンスではなく整数としてキャプチャするために使用できる属性がありますEnumValue。これを使用して、インスタンスを復元するときに正しい定数を再度見つけることができます。

 class CellSizeRelation(Option):
     # skipping your enum definition and __init__ here

     def __getstate__(self):
         # capture what is normally pickled
         state = self.__dict__.copy()
         # replace the `value` key (now an EnumValue instance), with it's index:
         state['value'] = state['value'].index
         # what we return here will be stored in the pickle
         return state

     def __setstate__(self, newstate):
         # re-create the EnumState instance based on the stored index
         newstate['value'] = self.Values[newstate['value']]
         # re-instate our __dict__ state from the pickled state
         self.__dict__.update(newstate)

したがって、通常、__getstate__インスタンスが存在しない場合__dict__はピクルされます。その のコピーを返すようになりましたが、そのインデックス (単純な整数) のインスタンス__dict__を交換しました。EnumValueアンピクルでは、通常、新しいインスタンスは、ピクルでキャプチャし__dict__たアンピクルで更新されますが、定義されたので、列挙型インデックスを正しいものに戻すことができます。__dict____setstate__EnumValue

于 2012-08-03T19:45:03.480 に答える
0

EnumValue「列挙型」オブジェクトid間の同一性に依存しています。Enumこれには、いくつかの利点と欠点があります。

主な利点は、2 つの呼び出しでEnum('A', 'B')異なる列挙型を定義できることです。そう:

osx = Enum('Jaguar', 'Tiger', 'Leopard')
bigcats = Enum('Jaguar', 'Tiger', 'Leopard')

OS X 10.4 をストライプ キリング マシンと区別したい場合、これは便利です。

しかしこれはまた、 pickle が unpicklesosxとを区別すると、それらは互いに異なるだけでなく、以前のandbigcatsのインスタンスとも区別されることを意味します。あなたがそれについて考えると、本当に今その周りに何かがあります。osxbigcats

したがって、ソリューションにハッキング ピクルスを含めることはできません。enumモジュールをハッキングする必要があります。

__cmp__自分にとって意味のあることを行うための合理的な方法を定義する必要がありEnumます。osx-vs.-bigcats の区別を捨てることができれば、それは簡単です。できない場合は、それを処理する別の方法が必要です (enum定義に明示的なタグ名を追加するか、オプションであるが暗黙のうちに自動インクリメントするカウンターを追加するなど)。

于 2012-08-03T19:44:30.077 に答える