Manera rápida de copiar el diccionario en Python

92

Tengo un programa de Python que funciona mucho con diccionarios. Tengo que hacer miles de copias de diccionarios. Necesito una copia tanto de las claves como del contenido asociado. La copia será editada y no debe estar vinculada al original (por ejemplo, los cambios en la copia no deben afectar al original).

Las claves son cadenas, los valores son enteros (0/1).

Actualmente utilizo una forma sencilla:

newDict = oldDict.copy()

Perfilar mi Código muestra que la operación de copia lleva la mayor parte del tiempo.

¿Existen alternativas más rápidas al dict.copy()método? ¿Qué sería más rápido?

Joern
fuente
1
Si el valor puede ser 0 o 1, ¿ boolsería una mejor opción que un int?
Samir Talwar
5
Y si necesita miles de copias, ¿las máscaras de bits funcionarían aún mejor?
Wooble
@Samir no está boolen Python nombrado de inttodos modos.
Santa
Sin embargo, estoy de acuerdo en que una máscara de bits podría ser más eficaz para ti (dependiendo de cómo uses este "dict", en realidad).
Santa
1
Para aclarar, el booltipo es en realidad una subclase (¿subtipo?) Del inttipo.
Santa

Respuestas:

64

Mirando la fuente C para las dictoperaciones de Python , puede ver que hacen una copia bastante ingenua (pero eficiente). Básicamente se reduce a una llamada a PyDict_Merge:

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

Esto hace las comprobaciones rápidas de cosas como si son el mismo objeto y si tienen objetos en ellos. Después de eso, hace un generoso cambio de tamaño / asignación al dict de destino y luego copia los elementos uno por uno. No veo que vayas mucho más rápido que el incorporado copy().

Daniel DiPaolo
fuente
1
Parece que será mejor que reescriba el código para evitar el uso de dictados, o usar una estructura de datos más rápida que pueda hacer el mismo trabajo. ¡Muchas gracias por la respuesta!
Joern
56

Aparentemente, dict.copy es más rápido, como dices.

[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
utdemir
fuente
¡Gracias por la comparación! Intentará reescribir el código para evitar el uso de la copia de dict en la mayoría de los lugares. ¡Gracias de nuevo!
Joern
4
La manera de hacer la última comparación, sin contar el costo de hacer la importación cada vez que está con timeit's -sargumento: python -m timeit -s "from copy import copy" "new = copy({1:1, 2:2, 3:3})". Mientras lo hace, extraiga también la creación de dict (para todos los ejemplos)
Thomas Wouters
Quizás repetir los procesos muchas veces es mejor, ya que puede haber algunas fluctuaciones de un disparo específico.
xiaohan2012
2
Timeit hace eso; como dice, se repite 1000000 veces y lo promedia.
utdemir
Tengo tiempos conflictivos. a = {b: b para b en el rango (10000)} En [5]:% timeit copy (a) 10000 bucles, mejor de 3: 186 µs por bucle En [6]:% timeit deepcopy (a) 100 bucles, mejor de 3: 14,1 ms por bucle En [7]:% de tiempo de copia () 1000 bucles, mejor de 3: 180 µs por bucle
Davoud Taghawi-Nejad
12

¿Puede proporcionar una muestra de código para que pueda ver cómo está usando copy () y en qué contexto?

Podrías usar

new = dict(old)

Pero no creo que sea más rápido.

MikeVaughan
fuente
5

Me doy cuenta de que este es un hilo antiguo, pero este es un resultado alto en los motores de búsqueda para "dict copy python", y el resultado superior para "dict copy performance", y creo que esto es relevante.

Desde Python 3.7, newDict = oldDict.copy()es hasta 5,5 veces más rápido que antes. Cabe destacar que, en este momento, newDict = dict(oldDict)no parece tener este aumento de rendimiento.

Hay un poco más de información aquí .

iandioch
fuente
3

Dependiendo de las cosas que deje a la especulación, es posible que desee ajustar el diccionario original y hacer una especie de copia por escrito.

La "copia" es entonces un diccionario que busca cosas en el diccionario "padre", si aún no contiene la clave --- pero contiene modificaciones en sí mismo.

Esto supone que no modificará el original y que las búsquedas adicionales no terminan costando más.

Alex Brasetvik
fuente
2

Sin embargo, las medidas dependen del tamaño del diccionario. Para 10000 entradas, copy (d) y d.copy () son casi lo mismo.

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
Davoud Taghawi-Nejad
fuente