如何在 python 中腌制嵌套类?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1947904/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I pickle a nested class in python?
提问by prinzdezibel
I have a nested class:
我有一个嵌套类:
class WidgetType(object): class FloatType(object): pass class TextType(object): pass
.. and an oject that refers the nested class type (not an instance of it) like this
.. 和一个像这样引用嵌套类类型(不是它的实例)的对象
class ObjectToPickle(object): def __init__(self): self.type = WidgetType.TextType
Trying to serialize an instance of the ObjectToPickle class results in:
尝试序列化 ObjectToPickle 类的实例会导致:
PicklingError: Can't pickle <class 'setmanager.app.site.widget_data_types.TextType'>
PicklingError:无法腌制 <class 'setmanager.app.site.widget_data_types.TextType'>
Is there a way to pickle nested classes in python?
有没有办法在python中腌制嵌套类?
回答by Nadia Alramli
The pickle module is trying to get the TextType class from the module. But since the class is nested it doesn't work. jasonjs's suggestion will work. Here are the lines in pickle.py responsible for the error message:
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)
will not work in the nested class case of course. To demonstrate what is going on try to add these lines before pickling the instance:
klass = getattr(mod, name)
当然在嵌套类的情况下不起作用。为了演示正在发生的事情,尝试在酸洗实例之前添加这些行:
import sys
setattr(sys.modules[__name__], 'TextType', WidgetType.TextType)
This code adds TextType as an attribute to the module. The pickling should work just fine. I don't advice you to use this hack though.
此代码将 TextType 作为属性添加到模块。酸洗应该可以正常工作。不过,我不建议您使用此 hack。
回答by pelson
I know this is a veryold question, but I have never explicitly seen a satisfactory solution to this question other than the obvious, and most likely correct, answer to re-structure your code.
我知道这是一个非常古老的问题,但我从来没有明确地看到过这个问题的令人满意的解决方案,除了明显且最有可能正确的答案来重新构建您的代码。
Unfortunately, it is not always practical to do such a thing, in which case as a very last resort, it ispossible to pickle instances of classes which are defined inside another class.
不幸的是,它并不总是可行的做这样的事情,在这种情况下,作为最后的手段,它是可能的泡菜被另一个类中定义的类的实例。
The python documentation for the __reduce__
functionstates that you can return
该__reduce__
函数的 python 文档指出您可以返回
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.
将被调用以创建对象的初始版本的可调用对象。元组的下一个元素将为这个可调用对象提供参数。
Therefore, all you need is an object which can return an instance of the appropriate class. This class mustitself be picklable (hence, must live on the __main__
level), and could be as simple as:
因此,您所需要的只是一个可以返回适当类的实例的对象。这个类本身必须是可腌制的(因此,必须存在于__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()
All that is left therefore, is to return the appropriate arguments in a __reduce__
method on FloatType:
因此,剩下的就是__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__, ))
The result is a class which is nested but instances can be pickled (further work is needed to dump/load the __state__
information, but this is relatively straightforward as per the __reduce__
documentation).
结果是一个嵌套的类,但实例可以被腌制(需要进一步的工作来转储/加载__state__
信息,但根据__reduce__
文档,这相对简单)。
This same technique (with slight code modifications) can be applied for deeply nested classes.
同样的技术(稍微修改代码)可以应用于深度嵌套的类。
A fully worked example:
一个完整的例子:
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
My final note on this is to remember what the other answers have said:
我对此的最后说明是记住其他答案所说的内容:
If you are in a position to do so, consider re-factoring your code to avoid the nested classes in the first place.
如果您有能力这样做,请首先考虑重构您的代码以避免嵌套类。
回答by Mike McKerns
If you use dill
instead of pickle
, it works.
如果您使用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'>
Get dill here: https://github.com/uqfoundation/dill
在这里获取莳萝:https: //github.com/uqfoundation/dill
回答by Hivert
In Sage (www.sagemath.org), we have many instances of this pickling issue. The way we decided to systematically solve it is to put the outerclass inside a specific metaclass whose goal is to implement and hide the hack. Note that this automatically propagate through nested classes if there are several level of nesting.
在 Sage ( www.sagemath.org) 中,我们有许多这种酸洗问题的实例。我们决定系统地解决它的方法是将外部类放在一个特定的元类中,其目标是实现和隐藏黑客。请注意,如果有多个嵌套级别,这会自动通过嵌套类传播。
回答by Jason S
Pickle only works with classes defined in module scope (top level). In this case, it looks like you could define the nested classes in module scope and then set them as properties on WidgetType, assuming there's a reason not to just reference TextType
and FloatType
in your code. Or, import the module they're in and use widget_type.TextType
and widget_type.FloatType
.
Pickle 仅适用于模块范围(顶级)中定义的类。在这种情况下,看起来您可以在模块范围内定义嵌套类,然后将它们设置为 WidgetType 上的属性,假设有理由不只是在您的代码中引用TextType
和FloatType
。或者,导入他们所在的模块并使用widget_type.TextType
and widget_type.FloatType
。
回答by kibitzer
Nadia's answer is pretty complete - it is practically not something you want to be doing; are you sure you can't use inheritance in WidgetTypes
instead of nested classes?
Nadia 的回答非常完整——这实际上不是你想做的事情;你确定你不能使用继承WidgetTypes
而不是嵌套类吗?
The only reason to use nested classes is to encapsulate classes working together closely, your specific example looks like an immediate inheritance candidate to me - there is no benefit in nesting WidgetType
classes together; put them in a module and inherit from the base WidgetType
instead.
使用嵌套类的唯一原因是将类紧密地封装在一起,您的具体示例对我来说看起来像是一个直接的继承候选者 - 将WidgetType
类嵌套在一起没有任何好处;将它们放在一个模块中并从基础继承WidgetType
。