ネストされたクラスがあります:
class WidgetType(object): class FloatType(object): pass class TextType(object): パス
..そしてこのようなネストされたクラスタイプ(そのインスタンスではない)を参照するオブジェクト
class ObjectToPickle(object): def __init __(self): self.type = WidgetType.TextType
ObjectToPickleクラスのインスタンスをシリアル化しようとすると、次のようになります。
PicklingError:<class'setmanager.app.site.widget_data_types.TextType '>をpickleできません
Pythonでネストされたクラスをpickle化する方法はありますか?
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)
はネストされたクラスの場合には機能しません。何が起こっているかを示すために、インスタンスをpickle化する前にこれらの行を追加してみてください。
_import sys
setattr(sys.modules[__name__], 'TextType', WidgetType.TextType)
_
このコードは、TextTypeを属性としてモジュールに追加します。酸洗いはうまくいくはずです。ただし、このハックを使用することはお勧めしません。
これがveryの古い質問であることは知っていますが、コードを再構築するための明白でおそらく正しい答え以外に、この質問に対する満足のいく解決策を明示的に見たことがありません。
残念ながら、そのようなことを行うことが常に実用的であるとは限りません。その場合、最後の手段として、別のクラス内で定義されているクラスのインスタンスをpickle化することが可能ですis。
python __reduce__
関数 のドキュメントはあなたが返すことができると述べています
オブジェクトの初期バージョンを作成するために呼び出される呼び出し可能オブジェクト。タプルの次の要素は、この呼び出し可能オブジェクトの引数を提供します。
したがって、必要なのは、適切なクラスのインスタンスを返すことができるオブジェクトだけです。このクラス必須それ自体が選択可能であり(したがって、__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()
したがって、残っているのは、FloatTypeの__reduce__
メソッドで適切な引数を返すことだけです。
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__, ))
結果はネストされたクラスですが、インスタンスをpickle化できます(__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
これに関する私の最後の注意は、他の答えが言ったことを覚えておくことです:
そうする立場にある場合は、そもそもネストされたクラスを回避するためにコードをリファクタリングすることを検討してください。
Sage( www.sagemath.org )には、この酸洗いの問題の多くの事例があります。それを体系的に解決することにした方法は、ハックを実装して非表示にすることを目的とする特定のメタクラス内にouterクラスを配置することです。複数のレベルのネストがある場合、これはネストされたクラスを介して自動的に伝播することに注意してください。
dill
の代わりにpickle
を使用すると、機能します。
>>> import dill
>>>
>>> class WidgetType(object):
... class FloatType(object):
... pass
... class TextType(object):
... pass
...
>>> class ObjectToPickle(object):
... def __init__(self):
... self.type = WidgetType.TextType
...
>>> x = ObjectToPickle()
>>>
>>> _x = dill.dumps(x)
>>> x_ = dill.loads(_x)
>>> x_
<__main__.ObjectToPickle object at 0x10b20a250>
>>> x_.type
<class '__main__.TextType'>
ここでディルを取得します: https://github.com/uqfoundation/dill
Pickleは、モジュールスコープ(トップレベル)で定義されたクラスでのみ機能します。この場合、コードでTextType
とFloatType
を参照しない理由があると仮定すると、モジュールスコープでネストされたクラスを定義し、それらをWidgetTypeのプロパティとして設定できるようです。または、それらが含まれているモジュールをインポートして、widget_type.TextType
およびwidget_type.FloatType
を使用します。
ナディアの答えはかなり完全です-それは実際にはあなたがやりたいことではありません。ネストされたクラスの代わりにWidgetTypes
で継承を使用できないのですか?
ネストされたクラスを使用する唯一の理由は、密接に連携するクラスをカプセル化することです。特定の例は、私には直接の継承候補のように見えます。WidgetType
クラスを一緒にネストするメリットはありません。それらをモジュールに入れ、代わりにベースWidgetType
から継承します。