Un modo veloce per copiare il dizionario in Python


92

Ho un programma Python che funziona molto con i dizionari. Devo fare copie di dizionari migliaia di volte. Ho bisogno di una copia sia delle chiavi che dei contenuti associati. La copia verrà modificata e non deve essere collegata all'originale (ad esempio, le modifiche nella copia non devono influire sull'originale).

Le chiavi sono stringhe, i valori sono numeri interi (0/1).

Attualmente uso un modo semplice:

newDict = oldDict.copy()

La profilazione del mio codice mostra che l'operazione di copia richiede la maggior parte del tempo.

Esistono alternative più veloci al dict.copy()metodo? Quale sarebbe il più veloce?


1
Se il valore può essere 0 o 1, sarebbe una boolscelta migliore di un int?
Samir Talwar

5
E se ti servissero migliaia di copie, le maschere di bit funzionerebbero ancora meglio?
Wooble

@Samir non si boolchiama intcomunque in Python .
Babbo Natale

Sono d'accordo, tuttavia, che una maschera di bit potrebbe essere più efficiente per te (dipende da come usi questo "dict", davvero).
Babbo Natale

1
Per chiarire, il booltipo è in realtà una sottoclasse (sottotipo?) Del inttipo.
Babbo Natale

Risposte:


64

Guardando il sorgente C per le dictoperazioni Python , puoi vedere che fanno una copia piuttosto ingenua (ma efficiente). Si riduce essenzialmente a una chiamata a PyDict_Merge:

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

Questo esegue i controlli rapidi per cose come se sono lo stesso oggetto e se contengono oggetti. Dopodiché esegue un generoso ridimensionamento / allocazione una tantum al dict di destinazione e quindi copia gli elementi uno per uno. Non ti vedo diventare molto più veloce del built-in copy().


1
Sembra che sia meglio riscrivere il codice per evitare del tutto l'uso di dettami o utilizzare una struttura dati più veloce che possa fare lo stesso lavoro. Grazie mille per la risposta!
Joern

56

Apparentemente dict.copy è più veloce, come dici tu.

[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

Grazie per il confronto! Cercherò di riscrivere il codice in modo da evitare l'uso della copia dei dict nella maggior parte dei posti. Grazie ancora!
Joern

4
Il modo di fare l'ultimo confronto senza contare il costo di fare l'importazione ogni volta è con timeit's -sargomento: python -m timeit -s "from copy import copy" "new = copy({1:1, 2:2, 3:3})". Già che ci sei, tira fuori anche la creazione dei dettami (per tutti gli esempi).
Thomas Wouters,

Forse ripetere i processi molte volte è meglio poiché potrebbero esserci alcune fluttuazioni di uno scatto specifico.
xiaohan2012

2
Timeit lo fa; come si dice, scorre 1000000 volte e ne calcola la media.
utdemir

Ho orari contrastanti. a = {b: b for b in range (10000)} In [5]:% timeit copy (a) 10000 loop, meglio di 3: 186 µs per loop In [6]:% timeit deepcopy (a) 100 loop, meglio di 3: 14,1 ms per loop In [7]:% timeit a.copy () 1000 loop, meglio di 3: 180 µs per loop
Davoud Taghawi-Nejad

12

Potete fornire un esempio di codice in modo che io possa vedere come state usando copy () e in quale contesto?

Potresti usare

new = dict(old)

Ma non credo che sarà più veloce.


5

Mi rendo conto che questo è un vecchio thread, ma questo è un risultato elevato nei motori di ricerca per "dict copy python" e il miglior risultato per "dict copy performance", e credo che questo sia rilevante.

Da Python 3.7, newDict = oldDict.copy()è fino a 5,5 volte più veloce di prima. In particolare, in questo momento, newDict = dict(oldDict)non sembra avere questo aumento delle prestazioni.

Ci sono un po 'più di informazioni qui .


3

A seconda delle cose che lasci alla speculazione, potresti voler avvolgere il dizionario originale e fare una sorta di copia su scrittura.

La "copia" è quindi un dizionario che cerca cose nel dizionario "genitore", se non contiene già la chiave --- ma riempie le modifiche in sé.

Ciò presuppone che non modifichi l'originale e che le ricerche extra non costino di più.


2

Tuttavia, le misurazioni dipendono dalla dimensione del dizionario. Per 10000 voci copy (d) e d.copy () sono quasi la stessa cosa.

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.