copy.deepcopy vs pickle

时间:2009-09-11 12:39:43

标签: python pickle deep-copy

我有一个小部件的树结构,例如集合包含模型,模型包含小部件。我想要复制整个集合,copy.deepcopy与'pickle and de pickle'对象相比更快,但是用C语言编写的cPickle要快得多,所以

  1. 为什么我(我们)总是不使用cPickle而不是深度复制?
  2. 还有其他副本吗?因为pickle比deepcopy慢,但cPickle更快,所以可能是深度复制的C实现将是胜利者
  3. 示例测试代码:

    import copy
    import pickle
    import cPickle
    
    class A(object): pass
    
    d = {}
    for i in range(1000):
        d[i] = A()
    
    def copy1():
        return copy.deepcopy(d)
    
    def copy2():
        return pickle.loads(pickle.dumps(d, -1))
    
    def copy3():
        return cPickle.loads(cPickle.dumps(d, -1))
    

    时序:

    >python -m timeit -s "import c" "c.copy1()"
    10 loops, best of 3: 46.3 msec per loop
    
    >python -m timeit -s "import c" "c.copy2()"
    10 loops, best of 3: 93.3 msec per loop
    
    >python -m timeit -s "import c" "c.copy3()"
    100 loops, best of 3: 17.1 msec per loop
    

5 个答案:

答案 0 :(得分:31)

问题是,pickle + unpickle可以更快(在C实现中),因为它比深度复制更不通用:许多对象可以被深度复制但不能被腌制。例如,假设您的班级A已更改为...:

class A(object):
  class B(object): pass
  def __init__(self): self.b = self.B()

现在,copy1仍然可以正常工作(A的复杂性会降低它的速度,但绝对不能阻止它); copy2copy3中断,堆栈跟踪的结尾说......:

  File "./c.py", line 20, in copy3
    return cPickle.loads(cPickle.dumps(d, -1))
PicklingError: Can't pickle <class 'c.B'>: attribute lookup c.B failed

即,酸洗总是假设类和函数是模块中的顶级实体,因此“按名称”腌制它们 - 深度复制绝对没有这样的假设。

因此,如果您遇到“有点深度复制”速度绝对至关重要的情况,那么每毫秒都很重要,并且您希望利用您知道的特殊限制应用于您正在复制的对象,例如通过各种方式使酸洗适用,或者有利于其他形式的序列化和其他快捷方式 - 但是如果你这样做,你必须意识到你正在限制你的系统永远存在这些限制,并记录设计决定非常清楚明确地为了未来的维护者的利益。

对于您需要通用性的NORMAL情况,请使用deepcopy! - )

答案 1 :(得分:6)

您应该使用deepcopy,因为它使您的代码更具可读性。使用序列化机制在内存中复制对象至少会让另一个阅读代码的开发人员感到困惑。使用深度复制还意味着您可以在深度复制中获得未来优化的好处。

优化的第一条规则:不要。

答案 2 :(得分:2)

总是cPickle比deepcopy()更快的情况。虽然cPickle可能总是比pickle更快,但它是否比deepcopy更快取决于

  • 要复制的结构的大小和嵌套级别,
  • 包含对象的类型,
  • 酸洗字符串表示的大小。

如果可以腌制某些东西,它显然可以进行深度复制,但事实并非如此:为了腌制某些东西,它需要完全序列化;这不是深度复制的情况。特别是,您可以通过在内存中复制结构(考虑扩展类型)非常有效地实现__deepcopy__,而无法将所有内容保存到磁盘。 (想想暂停到RAM和挂起到磁盘。)

满足上述条件的众所周知的扩展类型可能是ndarray,实际上,它可以作为您观察的一个很好的反例:使用d = numpy.arange(100000000),您的代码会提供不同的运行时:

In [1]: import copy, pickle, cPickle, numpy

In [2]: d = numpy.arange(100000000)

In [3]: %timeit pickle.loads(pickle.dumps(d, -1))
1 loops, best of 3: 2.95 s per loop

In [4]: %timeit cPickle.loads(cPickle.dumps(d, -1))
1 loops, best of 3: 2.37 s per loop

In [5]: %timeit copy.deepcopy(d)
1 loops, best of 3: 459 ms per loop

如果__deepcopy__未实施,copypickle共享公共基础设施(参见Relationship between pickle and deepcopy中讨论的copy_reg模块)。

答案 3 :(得分:1)

更快就是首先避免复制。你提到你正在渲染。为什么需要复制对象?

答案 4 :(得分:0)

简短而有些迟:

  • 如果你必须cPickle一个对象,你也可以使用cPickle方法深度复制(但文档)

e.g。你可能会考虑:

def mydeepcopy(obj):
    try:
       return cPickle.loads(cPickle.dumps(obj, -1))
    except PicklingError:
       return deepcopy(obj)