题
我有一个树形结构部件,例如集合包含模型和模型包含的部件。我想要复制的整个集合, copy.deepcopy
是,更快的比较'坐并de-坐'ing的对象,但cPickle作为正在编写C快得多,所以
- 为什么我不应该(们)始终使用cPickle而不是deepcopy?
- 是否有任何其他复制的选择吗?因为坐慢然后deepcopy但cPickle速度更快,所以可能是C执行deepcopy会是赢家
样品中测试的代码:
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
解决方案
问题是,pickle + unpickle可以更快(在C实现中),因为它比深度复制更不通用:许多对象可以被深度复制但不能被腌制。例如,假设您的类 A
已更改为...:
class A(object):
class B(object): pass
def __init__(self): self.b = self.B()
现在, copy1
仍然可以正常工作(A的复杂性会降低它的速度,但绝对不能阻止它); copy2
和 copy3
打破,堆栈跟踪的结尾说......:
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
即,酸洗总是假设类和函数是其模块中的顶级实体,因此“按名称”对它们进行腌制。 - deepcopying完全没有这样的假设。
因此,如果你的情况是“有点深度复制”的速度。绝对至关重要,每毫秒都很重要,并且您希望利用您知道应用于您正在复制的对象的特殊限制,例如那些使酸洗适用的,或者有利于其他形式的序列化和其他快捷方式的限制,所有方法都要继续 - 但如果你这样做,你必须意识到你正在限制你的系统永远存在这些限制,并且为了未来的维护者的利益,非常清楚明确地记录设计决策。
对于您需要通用性的NORMAL情况,请使用 deepcopy
! - )
其他提示
您应该使用deepcopy,因为它使您的代码更具可读性。使用序列化机制在内存中复制对象至少会让另一个阅读代码的开发人员感到困惑。使用深度复制还意味着您可以在深度复制中获得未来优化的好处。
优化的第一条规则:不要。
它是 不 总的情况,cPickle快于deepcopy().同时cPickle是可能永远快于坐,无论是快于deepcopy取决于
- 大小和排级结构复制,
- 该类型中包含的对象,
- 大腌串的代表性。
如果有什么可以被腌制的,它显然可以deepcopied,但相反的是不是这样的情况: 为了坐的东西,需要将充分地化;这个不是这种情况对于deepcopying.特别是,可以实现 __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__
是没有实现, copy
和 pickle
分享共同的基础设施(cf。 copy_reg
模块,讨论 之间的关系和坐deepcopy).
更快就是首先避免复制。你提到你正在渲染。为什么需要复制对象?
短和有些晚了:
- 如果你要cPickle一个对象不管怎么说,你可能会以及使用cPickle方法deepcopy(但文件)
例如你可能会考虑:
def mydeepcopy(obj):
try:
return cPickle.loads(cPickle.dumps(obj, -1))
except PicklingError:
return deepcopy(obj)