pri*_*bel 38 python nested class pickle
我有一个嵌套类:
class WidgetType(object): class FloatType(object): pass class TextType(object): pass
..和一个引用嵌套类类型(不是它的实例)的对象,就像这样
class ObjectToPickle(object): def __init__(self): self.type = WidgetType.TextType
尝试序列化ObjectToPickle类的实例会导致:
PicklingError:无法挑选<class'setmanager.app.site.widget_data_types.TextType'>
有没有办法在python中腌制嵌套类?
Nad*_*mli 29
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))
Run Code Online (Sandbox Code Playgroud)
klass = getattr(mod, name)
当然不会在嵌套类的情况下工作.为了演示正在发生的事情,尝试在pickle实例之前添加这些行:
import sys
setattr(sys.modules[__name__], 'TextType', WidgetType.TextType)
Run Code Online (Sandbox Code Playgroud)
此代码将TextType作为属性添加到模块中.酸洗应该工作得很好.我不建议你使用这个黑客.
pel*_*son 28
我知道这是一个非常古老的问题,但我从未明确地看到过这个问题的令人满意的解决方案,除了重新构造代码的明显且最可能正确的答案.
不幸的是,它并不总是可行的做这样的事情,在这种情况下,作为最后的手段,它是可能的泡菜被另一个类中定义的类的实例.
该__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()
Run Code Online (Sandbox Code Playgroud)
因此,剩下的就是__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__, ))
Run Code Online (Sandbox Code Playgroud)
结果是一个嵌套的类,但实例可以被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
Run Code Online (Sandbox Code Playgroud)
我对此的最后一点是要记住其他答案所说的内容:
如果您有能力这样做,请考虑重新分解代码以避免首先使用嵌套类.
小智 5
在Sage(www.sagemath.org)中,我们有很多这种酸洗问题.我们决定系统地解决它的方法是将外部类放在特定的元类中,其目标是实现和隐藏黑客.请注意,如果存在多个嵌套级别,则会自动通过嵌套类传播.
如果你使用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'>
Run Code Online (Sandbox Code Playgroud)
在这里获取莳萝:https://github.com/uqfoundation/dill
Pickle 仅适用于模块范围(顶层)中定义的类。在这种情况下,看起来您可以在模块范围中定义嵌套类,然后将它们设置为 WidgetType 上的属性,假设有理由不只在代码中引用TextType
and 。FloatType
或者,导入它们所在的模块并使用widget_type.TextType
和widget_type.FloatType
。