泡菜和深层镜之间的关系

时间:2014-03-13 19:22:50

标签: python pickle python-2.x deep-copy

picklecopy.deepcopy 之间的关系究竟是什么?他们分享了哪些机制,以及如何分享?

很明显,这两个是密切相关的操作,并分享了一些机制/协议,但我无法理解细节。

我发现的一些(令人困惑的)事情:

  1. 如果某个类定义了__[gs]etstate__,则会调用deepcopy个实例。这一开始让我感到惊讶,因为我认为它们特定于pickle,但后来我发现 Classes can use the same interfaces to control copying that they use to control pickling 。但是,没有关于在深度复制时如何使用 __[gs]etstate__的文档(如何使用从__getstate__返回的值,传递给__setstate__的内容?)< / LI>
  2. deepcopy的简单替代实施将是pickle.loads(pickle.dumps(obj))。但是,这不可能等同于深度复制,因为如果一个类定义了__deepcopy__操作,则不会使用这种基于pickle的deepcopy实现来调用它。 (我还偶然发现一个声明,即深度复制比pickle更普遍,并且有许多类型是深度可复制的,但不是可拾取的。)
  3. (1)表示共性,而(2)表示pickledeepcopy之间的差异。

    最重要的是,我发现了这两个相互矛盾的陈述:

      

    copy_reg:pickle,cPickle和copy模块在pickle /复制这些对象时使用这些函数

      

    copy模块不使用copy_reg注册模块

    这一方面,这是pickledeepcopy之间关系/共性的另一种表现,另一方面也助长了我的困惑......

    [我的经验是python2.7,但我也很感激有关python2和python3之间的pickle / deepcopy差异的任何指示]

2 个答案:

答案 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,应该启用copydeepcopy来完成他们的工作。

答案 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__来创建给定对象派生类的新实例。