How can I pickle a nested class in python?
我有一个嵌套类:
1 2 3 4 5 6 7 | class WidgetType(object): class FloatType(object): pass class TextType(object): pass |
…以及一个引用嵌套类类型(不是它的实例)的项目,如下所示
1 2 3 | class ObjectToPickle(object): def __init__(self): self.type = WidgetType.TextType |
尝试序列化ObjectTopickle类的实例会导致:
PicklingError: Can't pickle
有没有办法在python中pickle嵌套类?
pickle模块正在尝试从模块中获取texttype类。但由于类是嵌套的,所以它不起作用。詹森的建议会奏效的。pickle.py中的行负责错误消息:
1 2 3 4 5 6 7 8 | 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)) |
当然,
1 2 | import sys setattr(sys.modules[__name__], 'TextType', WidgetType.TextType) |
此代码将textType作为属性添加到模块中。腌制应该可以。不过我不建议你使用这个黑客。
我知道这是一个非常古老的问题,但是我从来没有明确地看到过这个问题的令人满意的解决方案,除了明显的、最可能是正确的、重新构造代码的答案。
不幸的是,做这样的事情并不总是实际的,在这种情况下,作为最后的手段,可以pickle在另一个类中定义的类的实例。
A callable object that will be called to create the initial version of the object. The next element of the tuple will provide arguments for this callable.
因此,您所需要的只是一个可以返回适当类的实例的对象。这个类本身必须是可挑选的(因此,必须生活在
1 2 3 4 5 6 7 8 9 10 11 | 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上
1 2 3 4 5 6 7 | 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(需要进一步的工作来转储/加载
同样的技术(稍加修改代码)也可以应用于深度嵌套的类。
一个充分发挥作用的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | 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 |
关于这一点,我的最后一点是要记住其他答案所说的:
If you are in a position to do so, consider re-factoring your code to
avoid the nested classes in the first place.
如果您使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | >>> 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'> |
在这里获取dill:https://github.com/uqfoundation/dill
在sage(www.sagemath.org)中,我们有许多关于酸洗问题的实例。我们决定系统地解决这个问题的方法是将外部类放在一个特定的元类中,这个元类的目标是实现和隐藏黑客。注意,如果存在多个嵌套级别,那么这将自动通过嵌套类传播。
pickle仅适用于模块范围(顶层)中定义的类。在这种情况下,您似乎可以在模块范围中定义嵌套类,然后将它们设置为widgetype上的属性,假设有理由不只是在代码中引用
纳迪亚的回答相当完整——实际上这不是你想做的事情;你确定不能在
使用嵌套类的唯一原因是将工作紧密的类封装在一起,对于我来说,您的特定示例看起来像是直接继承的候选者—将