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:无法挑选
'setmanager.app.site.widget_data_types.texttype'>
Is there a way to pickle nested classes in python?
有没有办法在python中pickle嵌套类?
6 个解决方案
#1
28
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)在嵌套类的情况下不起作用。为了演示正在发生的事情,尝试在pickle实例之前添加这些行:
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作为属性添加到模块中。酸洗应该工作得很好。我不建议你使用这个黑客。
#2
24
I know this is a very old 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 is possible to pickle instances of classes which are defined inside another class.
不幸的是,做这样的事情并不总是切实可行的,在这种情况下作为最后的手段,可以挑选在另一个类中定义的类的实例。
The python documentation for the __reduce__
function states 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 must itself 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:
因此,剩下的就是在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__, ))
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).
结果是一个嵌套的类,但实例可以被pickle(需要进一步的工作来转储/加载__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.
如果您有能力这样做,请考虑重新分解代码以避免嵌套类。
#3
5
If you use dill
instead of pickle
, it works.
如果你使用莳萝而不是泡菜,它的工作原理。
>>> 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
#4
5
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 outer class 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)中,我们有很多这种酸洗问题。我们决定系统地解决它的方法是将外部类放在一个特定的元类中,其目标是实现和隐藏黑客。请注意,如果存在多个嵌套级别,则会自动通过嵌套类传播。
#5
2
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和widget_type.FloatType。
#6
1
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继承。
#1
28
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)在嵌套类的情况下不起作用。为了演示正在发生的事情,尝试在pickle实例之前添加这些行:
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作为属性添加到模块中。酸洗应该工作得很好。我不建议你使用这个黑客。
#2
24
I know this is a very old 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 is possible to pickle instances of classes which are defined inside another class.
不幸的是,做这样的事情并不总是切实可行的,在这种情况下作为最后的手段,可以挑选在另一个类中定义的类的实例。
The python documentation for the __reduce__
function states 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 must itself 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:
因此,剩下的就是在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__, ))
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).
结果是一个嵌套的类,但实例可以被pickle(需要进一步的工作来转储/加载__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.
如果您有能力这样做,请考虑重新分解代码以避免嵌套类。
#3
5
If you use dill
instead of pickle
, it works.
如果你使用莳萝而不是泡菜,它的工作原理。
>>> 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
#4
5
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 outer class 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)中,我们有很多这种酸洗问题。我们决定系统地解决它的方法是将外部类放在一个特定的元类中,其目标是实现和隐藏黑客。请注意,如果存在多个嵌套级别,则会自动通过嵌套类传播。
#5
2
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和widget_type.FloatType。
#6
1
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继承。