pickle
和copy.deepcopy
之间的关系究竟是什么?他们分享了哪些机制,以及如何分享?
很明显,这两个是密切相关的操作,并分享了一些机制/协议,但我无法理解细节。
我发现的一些(令人困惑的)事情:
__[gs]etstate__
,则会调用deepcopy
个实例。这一开始让我感到惊讶,因为我认为它们特定于pickle
,但后来我发现 Classes can use the same interfaces to control copying that they use to control pickling 。但是,没有关于在深度复制时如何使用 __[gs]etstate__
的文档(如何使用从__getstate__
返回的值,传递给__setstate__
的内容?)< / LI>
deepcopy
的简单替代实施将是pickle.loads(pickle.dumps(obj))
。但是,这不可能等同于深度复制,因为如果一个类定义了__deepcopy__
操作,则不会使用这种基于pickle的deepcopy实现来调用它。 (我还偶然发现一个声明,即深度复制比pickle更普遍,并且有许多类型是深度可复制的,但不是可拾取的。)(1)表示共性,而(2)表示pickle
和deepcopy
之间的差异。
最重要的是,我发现了这两个相互矛盾的陈述:
copy_reg:pickle,cPickle和copy模块在pickle /复制这些对象时使用这些函数
和
copy模块不使用copy_reg注册模块
这一方面,这是pickle
和deepcopy
之间关系/共性的另一种表现,另一方面也助长了我的困惑......
[我的经验是python2.7,但我也很感激有关python2和python3之间的pickle / deepcopy差异的任何指示]
答案 0 :(得分:7)
好的,我必须阅读这个源代码,但看起来这是一个非常简单的答案。 http://svn.python.org/projects/python/trunk/Lib/copy.py
copy
查找一些内置类型,它知道构造函数的样子(在_copy_dispatch
字典中注册,并且当它不知道如何复制基本类型时,它导入copy_reg.dispatch_table
...这是pickle
注册它所知道的用于生成对象副本的方法的地方。本质上,它是对象类型的字典和&#34 ;生成一个新对象的函数&#34; - 这个&#34;函数用于生成一个新对象&#34;几乎是你在编写__reduce__
或__reduce_ex__
方法时所写的内容一个对象(如果其中一个缺失或需要帮助,它会遵循__setstate__
,__getstate__
等方法。
这就是copy
。基本上......(附加一些条款......)
def copy(x):
"""Shallow copy operation on arbitrary Python objects.
See the module's __doc__ string for more info.
"""
cls = type(x)
copier = _copy_dispatch.get(cls)
if copier:
return copier(x)
copier = getattr(cls, "__copy__", None)
if copier:
return copier(x)
reductor = dispatch_table.get(cls)
if reductor:
rv = reductor(x)
else:
reductor = getattr(x, "__reduce_ex__", None)
if reductor:
rv = reductor(2)
else:
reductor = getattr(x, "__reduce__", None)
if reductor:
rv = reductor()
else:
raise Error("un(shallow)copyable object of type %s" % cls)
deepcopy
执行与上面相同的操作,但另外检查每个对象并确保每个新对象都有一个副本,而不是指针引用。 deepcopy
构建它自己的_deepcopy_dispatch
表(一个字典),它在其中注册函数,确保生成的新对象没有对原始的指针引用(可能使用__reduce__
生成在copy_reg.dispatch_table
)
因此,编写__reduce__
方法(或类似方法)并将其注册到copy_reg
,应该启用copy
和deepcopy
来完成他们的工作。
答案 1 :(得分:6)
你不应该对(1)和(2)感到困惑。通常,Python会尝试为缺少的方法包含合理的回退。 (例如,定义__getitem__
以便拥有可迭代类就足够了,但实现__iter__
可能更有效。类似于__add__
等操作,带有可选{ {1}}等。)
__iadd__
是__deepcopy__
寻找的最专业的方法,但如果它不存在,那么回退到pickle协议是明智之举。它并不真正调用deepcopy()
/ dumps()
,因为它不依赖于中间表示为字符串,但它会间接使用loads()
和__getstate__
(正如您所观察到的那样,通过__setstate__
)。
目前,the documentation仍然存在
...复制模块不使用copy_reg注册模块。
但似乎是a bug that has been fixed in the meantime(可能2.7分支在这里没有得到足够的重视)。
另请注意,这已深入集成到Python中(至少现在如此); __reduce__
类本身实现object
(及其版本化的_ex变体),它引用__reduce__
来创建给定对象派生类的新实例。