Python 如果我使用派生类,我可以“pickle 本地对象”吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/36994839/
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
I can "pickle local objects" if I use a derived class?
提问by fonini
The pickle
reference states thatthe set of objects which can be pickled is rather limited. Indeed, I have a function which returns a dinamically-generated class, and I found I can't pickle instances of that class:
该pickle
参考资料指出,可以腌制的对象集相当有限。事实上,我有一个函数返回一个动态生成的类,我发现我不能pickle那个类的实例:
>>> import pickle
>>> def f():
... class A: pass
... return A
...
>>> LocalA = f()
>>> la = LocalA()
>>> with open('testing.pickle', 'wb') as f:
... pickle.dump(la, f, pickle.HIGHEST_PROTOCOL)
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
AttributeError: Can't pickle local object 'f.<locals>.A'
Such objects are too complicated for pickle
. Ok. Now, what's magic is that, if I try to pickle a similar object, but of a derived class, it works!
这样的对象对于 来说太复杂了pickle
。好的。现在,神奇的是,如果我尝试腌制一个类似的对象,但它是派生类的,它会起作用!
>>> class DerivedA(LocalA): pass
...
>>> da = DerivedA()
>>> with open('testing.pickle', 'wb') as f:
... pickle.dump(da, f, pickle.HIGHEST_PROTOCOL)
...
>>>
What's happening here? If this is so easy, why doesn't pickle
use this workaround to implement a dump
method that allows "local objects" to be pickled?
这里发生了什么事?如果这很简单,为什么不pickle
使用此解决方法来实现dump
允许腌制“本地对象”的方法?
回答by Akshat Mahajan
I think you did not read the reference you citecarefully. The reference also clearly states that only the following objects are pickleable:
我认为您没有仔细阅读您引用的参考资料。该参考还明确指出,只有以下对象是可腌制的:
- functions defined at the top level of a module (using def, not >lambda)
- built-in functions defined at the top level of a module
- classes that are defined at the top level of a module
- 在模块顶层定义的函数(使用 def,而不是 >lambda)
- 定义在模块顶层的内置函数
- 在模块顶层定义的类
Your example
你的榜样
>>> def f():
... class A: pass
... return A
does not define a class at the top level of a module, it defines a class within the scopeof f()
. pickle
works on global classes, not local classes. This automatically fails the pickleable test.
在模块的顶部电平不定义类,它定义了一个内类范围的f()
。pickle
适用于全局类,而不是本地类。这会自动使 pickleable 测试失败。
DerivedA
is a global class, so all is well.
DerivedA
是一个全局类,所以一切都很好。
As for why only top-level (global to you) classes and functions can't be pickled, the reference answers that question as well (bold mine):
至于为什么只有顶级(对你来说是全局的)类和函数不能被腌制,参考资料也回答了这个问题(大胆的我的):
Note that functions (built-in and user-defined) are pickled by “fully qualified” name reference, not by value. This means that only the function name is pickled, along with the name of the module the function is defined in. Neither the function's code, nor any of its function attributes are pickled. Thus the defining module must be importable in the unpickling environment, and the module must contain the named object, otherwise an exception will be raised.
Similarly, classes are pickled by named reference, so the same restrictions in the unpickling environment apply.
请注意,函数(内置和用户定义的)由“完全限定”名称引用而不是值进行腌制。这意味着只有函数名和定义函数的模块的名称被腌制。函数的代码和它的任何函数属性都没有被腌制。因此定义模块必须在 unpickling 环境中可导入,并且模块必须包含命名对象,否则将引发异常。
类似地,类通过命名引用进行酸洗,因此在 unpickling 环境中适用相同的限制。
So there you have it. pickle
only serialises objects by name reference, not by the raw instructions contained within the object. This is because pickle's
job is to serialise object hierarchy, and nothing else.
所以你有它。pickle
仅通过名称引用序列化对象,而不是通过对象中包含的原始指令序列化。这是因为pickle's
工作是序列化对象层次结构,没有别的。
回答by Mike McKerns
I disagree, you can pickle both. You just need to use a better serializer, like dill
. dill
(by default) pickles classes by saving the class definition instead of pickling by reference, so it won't fail your first case. You can even use dill
to get the source code, if you like.
我不同意,你可以腌制两者。您只需要使用更好的序列化程序,例如dill
. dill
(默认情况下)通过保存类定义而不是通过引用腌制来腌制类,因此它不会使您的第一种情况失败。dill
如果您愿意,您甚至可以使用来获取源代码。
>>> import dill as pickle
>>> def f():
... class A: pass
... return A
...
>>> localA = f()
>>> la = localA()
>>>
>>> _la = pickle.dumps(la)
>>> la_ = pickle.loads(_la)
>>>
>>> class DerivedA(localA): pass
...
>>> da = DerivedA()
>>> _da = pickle.dumps(da)
>>> da_ = pickle.loads(_da)
>>>
>>> print(pickle.source.getsource(la_.__class__))
class A: pass
>>>
回答by haael
You can only pickle instances of classes defined at module's top level.
您只能腌制在模块顶级定义的类的实例。
However, you can pickle instances of locally-defined classes if you promote them to top level.
但是,如果将本地定义的类的实例提升到顶级,则可以选择它们。
You must set the __ qualname__class attribute of the local class. Then you must assign the class to a top-level variable of the same name.
您必须设置本地类的__ qualname__类属性。然后,您必须将该类分配给同名的顶级变量。
def define_class(name):
class local_class:
pass
local_class.__qualname__ = name
return local_class
class_A = define_class('class_A') # picklable
class_B = define_class('class_B') # picklable
class_X = define_class('class_Y') # unpicklable, names don't match
回答by user2357112 supports Monica
DerivedA
instances are pickleable because DerivedA
is available through a global variable matching its fully-qualified name, which is how pickle
looks for classes when unpickling.
DerivedA
实例是可pickle 的,因为DerivedA
它可以通过匹配其完全限定名称的全局变量获得,这就是pickle
unpickling 时查找类的方式。
The problem with trying to do something like this with local classes is that there's nothing identifying whichA
class an instance corresponds to. If you run f
twice, you get two A
classes, and there's no way to tell which one should be the class of unpickled A
instances from another run of the program. If you don't run f
at all, you get noA
classes, and then what the heck do you do about the type of unpickled instances?
尝试用本地类做这样的事情的问题是没有什么可以识别实例对应于哪个A
类。如果你运行f
两次,你会得到两个A
类,并且无法A
从程序的另一次运行中判断哪一个应该是 unpickled实例的类。如果你根本不运行f
,你就不会得到任何A
类,然后你对 unpickled 实例的类型做了什么?