Python中快速复制字典的方法

93
我有一个使用字典频繁的Python程序。我需要复制字典数千次。我需要复制字典的键和相关内容。这个副本将被编辑,必须与原始副本不相关(例如,在副本中进行的更改不应该影响到原始副本)。
键是字符串,值是整数(0/1)。
我目前使用简单的方法:
newDict = oldDict.copy()

我的代码分析显示,复制操作占用了大部分时间。

有没有比 dict.copy() 方法更快的替代方案?最快的方法是什么?


1
如果值只能为0或1,使用boolint更好吗? - Samir Talwar
5
如果你需要成千上万份副本,位掩码是否更适用? - Wooble
@Samir 在 Python 中,bool 不是被称为 int 吗? - Santa
@Santa:就我所知,它们是完全不同的类型。 - Samir Talwar
1
澄清一下,bool类型实际上是int类型的子类(子类型?)。 - Santa
显示剩余4条评论
7个回答

67

查看Python dict操作的C源代码,可以看到它们执行了一种相当简单但高效的复制。它基本上归结为调用PyDict_Merge

PyDict_Merge(PyObject *a, PyObject *b, int override)

该函数进行了快速检查,例如它们是否为相同对象以及它们是否包含对象。之后,它会一次性地重新调整大小/分配目标字典,然后逐个复制元素。我认为你不可能比内置的copy()更快。


1
听起来我最好重新编写代码,避免使用字典 - 或者使用一个能够完成相同工作的更快的数据结构。非常感谢你的答复! - Joern

59

显然,正如你所说,dict.copy更快。

[utdmr@utdmr-arch ~]$ python -m timeit -s "d={1:1, 2:2, 3:3}" "new = d.copy()"
1000000 loops, best of 3: 0.238 usec per loop
[utdmr@utdmr-arch ~]$ python -m timeit -s "d={1:1, 2:2, 3:3}" "new = dict(d)"
1000000 loops, best of 3: 0.621 usec per loop
[utdmr@utdmr-arch ~]$ python -m timeit -s "from copy import copy; d={1:1, 2:2, 3:3}" "new = copy(d)"
1000000 loops, best of 3: 1.58 usec per loop

4
在不需要每次计算导入成本的情况下进行最后一次比较的方法是使用 timeit-s 参数:python -m timeit -s "from copy import copy; d = {1:1, 2:2, 3:3}" "new = copy(d)"。顺便提一下,对于所有示例,都要将字典创建操作分离出来。 - Thomas Wouters
也许重复多次流程会更好,因为某个特定的拍摄可能会有一些波动。 - xiaohan2012
2
Timeit就是这样做的;它会循环1000000次并取平均值。 - utdemir
我有时间冲突。a = {b: b for b in range(10000)}在[5]中:%timeit copy(a)每个循环的10000个循环,最佳3个:186微秒在[6]中:%timeit deepcopy(a)每个循环的100个循环,最佳3个:14.1毫秒在[7]中:%timeit a.copy()每个循环的1000个循环,最佳3个:180微秒 - Davoud Taghawi-Nejad
我的回答已经超过四年了,很可能CPython的实现和行为已经发生了很大的变化。如果你能为每一个获取时间,我将很乐意更新答案。 - utdemir
显示剩余2条评论

12

你能提供一个代码示例,这样我就可以看到你如何在什么上下文中使用copy()吗?

你可以使用

new = dict(old)

但我认为这并不会更快。


12

我知道这是一个老帖子,但它在搜索引擎中的“dict copy python”和“dict copy performance”的搜索结果中排名较高,我认为这仍然是相关的。从Python 3.7开始,newDict = oldDict.copy() 的速度比以前快了5.5倍。值得注意的是,目前使用 newDict = dict(oldDict) 似乎没有这种性能提升。

有更多信息,请点击此处


4

根据留给揣测的事情,您可能希望包装原始字典并进行一种类似于写时复制的操作。

"拷贝"是一个字典,如果它本身不包含键,则在"父级"字典中查找内容,但将修改的内容存储在自己内部。

这假设您不会修改原始内容,并且额外的查找不会导致更多成本。


3

但是,测量结果取决于字典大小。对于10000个条目,copy(d)和d.copy()几乎相同。

a = {b: b for b in range(10000)} 
In [5]: %timeit copy(a)
10000 loops, best of 3: 186 µs per loop
In [6]: %timeit deepcopy(a)
100 loops, best of 3: 14.1 ms per loop
In [7]: %timeit a.copy()
1000 loops, best of 3: 180 µs per loop

0
我刚遇到了这个问题。通过将键和值分开,我成功地转换成了列表,并且这对我帮助很大。
假设我有:
d = {
    'a': 5
    'b': 6
}

而且,我想要创建许多具有不同值但相同键的副本。
我可以创建一个查找表:
lookup_table = {
    'a': 0
    'b': 1
}

然后,使用一个列表:
counts = [5, 6]

获取一个值,我这样做:
index = lookup_table['a']
print(counts[index])

为了进行写时复制,我会做类似以下的操作:
index = lookup_table['a']
new_counts = counts.copy()
new_counts[index] -= 1

我尝试着使用Python的原生array模块来代替列表,但实际上它对我来说稍微慢了一些。不过,如果你要存储大量的数据,这可能是合适的选择。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接