41

ネストされたクラスがあります:

クラスWidgetType(object):
    
    クラスFloatType(object):
        パス
    
    クラスTextType(object):
        パス

..そしてこのようなネストされたクラスタイプ(そのインスタンスではない)を参照するオブジェクト

クラスObjectToPickle(object):
     def __init __(self):
         self.type = WidgetType.TextType

ObjectToPickleクラスのインスタンスをシリアル化しようとすると、次のようになります。

PicklingError:ピクルスできません<class'setmanager.app.site.widget_data_types.TextType'>

Pythonでネストされたクラスをピクルスにする方法はありますか?

4

7 に答える 7

32

これは非常に古い質問であることは知っていますが、コードを再構築するための明白でおそらく正しい答え以外に、この質問に対する満足のいく解決策を明示的に見たことはありません。

残念ながら、そのようなことを行うことは必ずしも実用的ではありません。その場合、最後の手段として、別のクラス内で定義されているクラスのインスタンスをピクルスにすることができます。

__reduce__関数のPythonドキュメントには、返すことができると記載されています

オブジェクトの初期バージョンを作成するために呼び出される呼び出し可能オブジェクト。タプルの次の要素は、この呼び出し可能オブジェクトの引数を提供します。

したがって、必要なのは、適切なクラスのインスタンスを返すことができるオブジェクトだけです。このクラスそれ自体が選択可能である必要があり(したがって、__main__レベルに存在する必要があります)、次のように単純にすることができます。

class _NestedClassGetter(object):
    """
    When called with the containing class as the first argument, 
    and the name of the nested class as the second argument,
    returns an instance of the nested class.
    """
    def __call__(self, containing_class, class_name):
        nested_class = getattr(containing_class, class_name)
        # return an instance of a nested_class. Some more intelligence could be
        # applied for class construction if necessary.
        return nested_class()

したがって、残っているのは、__reduce__FloatTypeのメソッドで適切な引数を返すことだけです。

class WidgetType(object):

    class FloatType(object):
        def __reduce__(self):
            # return a class which can return this class when called with the 
            # appropriate tuple of arguments
            return (_NestedClassGetter(), (WidgetType, self.__class__.__name__, ))

結果はネストされたクラスですが、インスタンスをピクルスにすることができます(情報をダンプ/ロードするにはさらに作業が必要ですが、これはドキュメント__state__によると比較的簡単です)。__reduce__

これと同じ手法(コードを少し変更するだけ)は、深くネストされたクラスに適用できます。

完全に機能する例:

import pickle


class ParentClass(object):

    class NestedClass(object):
        def __init__(self, var1):
            self.var1 = var1

        def __reduce__(self):
            state = self.__dict__.copy()
            return (_NestedClassGetter(), 
                    (ParentClass, self.__class__.__name__, ), 
                    state,
                    )


class _NestedClassGetter(object):
    """
    When called with the containing class as the first argument, 
    and the name of the nested class as the second argument,
    returns an instance of the nested class.
    """
    def __call__(self, containing_class, class_name):
        nested_class = getattr(containing_class, class_name)

        # make an instance of a simple object (this one will do), for which we can change the
        # __class__ later on.
        nested_instance = _NestedClassGetter()

        # set the class of the instance, the __init__ will never be called on the class
        # but the original state will be set later on by pickle.
        nested_instance.__class__ = nested_class
        return nested_instance



if __name__ == '__main__':

    orig = ParentClass.NestedClass(var1=['hello', 'world'])

    pickle.dump(orig, open('simple.pickle', 'w'))

    pickled = pickle.load(open('simple.pickle', 'r'))

    print type(pickled)
    print pickled.var1

これに関する私の最後の注意は、他の答えが言ったことを覚えておくことです:

そうする立場にある場合は、そもそもネストされたクラスを回避するためにコードをリファクタリングすることを検討してください。

于 2012-07-15T16:50:45.903 に答える
30

pickle モジュールは、モジュールから TextType クラスを取得しようとしています。しかし、クラスがネストされているため、機能しません。jasonjs の提案が機能します。エラー メッセージの原因となっている pickle.py の行は次のとおりです。

    try:
        __import__(module)
        mod = sys.modules[module]
        klass = getattr(mod, name)
    except (ImportError, KeyError, AttributeError):
        raise PicklingError(
            "Can't pickle %r: it's not found as %s.%s" %
            (obj, module, name))

klass = getattr(mod, name)もちろん、ネストされたクラスの場合は機能しません。何が起こっているかを示すために、インスタンスをピクルする前に次の行を追加してみてください。

import sys
setattr(sys.modules[__name__], 'TextType', WidgetType.TextType)

このコードは、TextType を属性としてモジュールに追加します。ピクルスはうまくいくはずです。ただし、このハックを使用することはお勧めしません。

于 2009-12-22T17:51:23.100 に答える
4

Sage ( www.sagemath.org ) では、このピッキングの問題の多くのインスタンスがあります。これを体系的に解決するために決定した方法は、ハッキングを実装して隠すことを目標とする特定のメタクラス内に外側のクラスを配置することです。複数レベルのネストがある場合、これはネストされたクラスに自動的に伝播することに注意してください。

于 2010-03-06T10:41:46.163 に答える
2

Pickle は、モジュール スコープ (トップ レベル) で定義されたクラスでのみ機能します。この場合、ネストされたクラスをモジュール スコープで定義し、それらを WidgetType のプロパティとして設定できるように見えます。これは、単にコード内で参照TextTypeしない理由があると仮定した場合です。FloatTypeまたは、それらが含まれているモジュールをインポートして使用widget_type.TextTypeし、widget_type.FloatType.

于 2009-12-22T17:36:12.227 に答える
1

ナディアの答えはかなり完全です - それは実際にはあなたがやりたいことではありません。WidgetTypesネストされたクラスの代わりに継承を使用できないと確信していますか?

ネストされたクラスを使用する唯一の理由は、緊密に連携するクラスをカプセル化することです。特定の例は、私にとって直接の継承候補のように見えます-WidgetTypeクラスを一緒にネストするメリットはありません。WidgetTypeそれらをモジュールに入れて、代わりにベースから継承します。

于 2009-12-22T21:19:04.663 に答える