泡菜和深层镜之间的关系

shx*_*hx2 21 python pickle deep-copy python-2.x

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

很明显这两个是密切相关的操作,并分享了一些机制/协议,但我不能围绕细节.

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

  1. 如果一个类定义__[gs]etstate__,则会调用它们deepcopy的实例.这让我感到惊讶,因为我认为它们是特定的pickle,但后来我发现Classes可以使用相同的接口来控制用于控制酸洗的复制.但是,没有关于 __[gs]etstate__深度复制时如何使用的文档(如何使用返回的值__getstate__,传递给__setstate__什么?)
  2. 一个天真的替代实现deepcopy将是pickle.loads(pickle.dumps(obj)).但是,这不可能等同于deepcopy',因为如果一个类定义了一个__deepcopy__操作,那么使用这个基于pickle的deepcopy实现就不会调用它.(我还偶然发现了一个声明,即深度复制比pickle更普遍,并且有许多类型是深度可复制的,但不是pickleable.)

(1)表示共性,而(2)表示pickle和之间的差异deepcopy.

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

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

复制模块不使用copy_reg注册模块

对此,一方面,是之间的关系/共性的另一个指示pickledeepcopy,而在另一方面,有助于我的困惑......

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

han*_*ine 8

你不应该被(1)和(2)搞糊涂.通常,Python会尝试为缺少的方法包含合理的回退.(例如,定义__getitem__以便具有可迭代类就足够了,但也可以更有效地实现__iter__.类似于操作,例如__add__,可选__iadd__等)

__deepcopy__是最专业的方法deepcopy(),但如果它不存在,回到pickle协议是一件明智的事情.它并不真正调用dumps()/ loads(),因为它不依赖于中间表示为字符串,但它会间接地使用__getstate____setstate__(经过__reduce__),如您所观察到的那样.

目前,文档仍然存在

...复制模块不使用copy_reg注册模块.

但这似乎是一个已经修复的错误(可能,2.7分支在这里没有得到足够的重视).

还要注意,这已经非常深入地集成到Python中(至少现在如此); 所述object类本身实现__reduce__(和它的版本化_ex变体),它是指copy_reg.__newobj__用于产生给定的派生对象的类的新鲜实例.


Mik*_*rns 7

好的,我必须阅读这个的源代码,但看起来这是一个非常简单的答案. http://svn.python.org/projects/python/trunk/Lib/copy.py

copy查找一些内置类型,它知道构造函数的样子(在_copy_dispatch字典中注册,当它不知道如何复制基本类型时,它会导入copy_reg.dispatch_table...这是pickle注册它知道的方法的地方用于生成对象的新副本.本质上,它是一个对象类型的字典和"生成一个新对象的函数" - 这个"生成一个新对象的函数"几乎就是你写一个__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)
Run Code Online (Sandbox Code Playgroud)

deepcopy做与上面相同的事情,但另外检查每个对象并确保每个新对象都有一个副本而不是指针引用. deepcopy构建它自己的_deepcopy_dispatch表(一个字典),它注册函数,确保生成的新对象没有对原始的指针引用(可能使用__reduce__注册的函数生成copy_reg.dispatch_table)

因此,编写一个__reduce__方法(或类似的方法)并将其注册copy_reg,应该能够实现copydeepcopy完成它们的工作.

  • 所以看起来"复制不使用copy_reg"有点谎言.它在封面下使用它.:) (2认同)