"Pickle和Deepcopy之间的关系"

2024-09-30 14:20:17 发布

您现在位置:Python中文网/ 问答频道 /正文

pickle和{}之间到底是什么关系?它们共享什么机制,以及如何共享?在

很明显,这两个操作是密切相关的,并且共享一些机制/协议,但是我不能把我的头脑集中在细节上。在

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

  1. 如果一个类定义了__[gs]etstate__,它们将被调用其实例的deepcopy。一开始这让我很惊讶,因为我认为它们是pickle特有的,但后来我发现了Classes can use the same interfaces to control copying that they use to control pickling。然而,对于深度复制(如何使用__getstate__返回的值,传递给__setstate__的值是如何使用的)时,如何使用__[gs]etstate__的文档在
  2. deepcopy的另一个简单的实现是pickle.loads(pickle.dumps(obj))。但是,这不可能等同于deepcopy'ing,因为如果一个类定义了一个__deepcopy__操作,那么就不会使用deepcopy的这种基于pickle的实现来调用它。(我还偶然发现了一个声明,deepcopy比pickle更通用,而且有许多类型可以deepcopy,但不能pickle。)

(1)表示共性,而(2)表示pickle和{}之间的区别。在

除此之外,我还发现了两种相互矛盾的说法:

copy_reg: The pickle, cPickle, and copy modules use those functions when pickling/copying those objects

以及

The copy module does not use the copy_reg registration module

一方面,这是pickle和{}之间的关系/共同性的另一个迹象,另一方面,也导致了我的困惑。。。在

[我的经验是使用python2.7,但我也希望能提供关于python2和python3在pickle/deepcopy上的差异的任何建议]


Tags: thetogs定义关系useregpickle
2条回答

你不应该被(1)和(2)混淆。一般来说,Python会尝试为丢失的方法包含合理的回退。(例如,定义__getitem__以拥有iterable类就足够了,但是实现__iter__可能更有效。类似于__add__,可选的__iadd__等。)

__deepcopy__deepcopy()要寻找的最专门的方法,但是如果它不存在,返回pickle协议是一件明智的事情。它并没有真正地调用dumps()/loads(),因为它不依赖中间表示作为字符串,而是间接地使用__getstate__和{}(通过__reduce__)。在

当前,the documentation仍处于状态

… The copy module does not use the copy_reg registration module.

但这似乎是a bug that has been fixed in the meantime(可能,2.7分支在这里没有得到足够的关注)。在

还要注意的是,这已经非常深入地集成到Python中了(至少现在是这样)object类本身实现了__reduce__(及其版本化的_-ex-variant),它引用{}来创建给定对象派生类的新实例。在

好吧,我不得不读了这篇文章的源代码,但看起来这是一个非常简单的答案。 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)

deepcopy执行与上述相同的操作,但除此之外,还要检查每个对象并确保每个新对象都有一个副本,而不是指针引用。deepcopy构建它自己的_deepcopy_dispatch表(dict),它在其中注册函数,以确保生成的新对象没有指向原始对象的指针引用(可能是用copy_reg.dispatch_table中注册的__reduce__函数生成的)

因此,编写一个__reduce__方法(或类似方法)并将其注册到copy_reg中,应该可以使copy和{}也能完成它们的工作。在

相关问题 更多 >