Schneller Weg, um Wörterbuch in Python zu kopieren

92

Ich habe ein Python-Programm, das viel mit Wörterbüchern arbeitet. Ich muss tausende Male Kopien von Wörterbüchern machen. Ich benötige eine Kopie der Schlüssel und der zugehörigen Inhalte. Die Kopie wird bearbeitet und darf nicht mit dem Original verknüpft werden (z. B. dürfen Änderungen an der Kopie keine Auswirkungen auf das Original haben.)

Schlüssel sind Zeichenfolgen, Werte sind Ganzzahlen (0/1).

Ich benutze derzeit einen einfachen Weg:

newDict = oldDict.copy()

Das Profilieren meines Codes zeigt, dass der Kopiervorgang die meiste Zeit in Anspruch nimmt.

Gibt es schnellere Alternativen zur dict.copy()Methode? Was wäre am schnellsten?

Joern
quelle
1
Wenn der Wert entweder 0 oder 1 sein kann, wäre a booleine bessere Wahl als ein int?
Samir Talwar
5
Und wenn Sie Tausende von Kopien davon benötigen, würden Bitmasken noch besser funktionieren?
Wooble
@Samir ist boolin Python intsowieso nicht benannt .
Santa
Ich stimme jedoch zu, dass eine Bitmaske für Sie effizienter sein könnte (je nachdem, wie Sie dieses "Diktat" wirklich verwenden).
Santa
1
Zur Verdeutlichung ist der boolTyp tatsächlich eine Unterklasse (Subtyp?) Des intTyps.
Santa

Antworten:

64

Wenn Sie sich die C-Quelle für die Python- dictOperationen ansehen, können Sie sehen, dass sie eine ziemlich naive (aber effiziente) Kopie erstellen. Es läuft im Wesentlichen auf einen Anruf an PyDict_Merge:

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

Auf diese Weise wird schnell überprüft, ob es sich um dasselbe Objekt handelt und ob Objekte darin enthalten sind. Danach führt es eine großzügige einmalige Größenänderung / Zuordnung zum Ziel-Diktat durch und kopiert dann die Elemente nacheinander. Ich sehe nicht, dass du viel schneller wirst als der eingebaute copy().

Daniel DiPaolo
quelle
1
Klingt so, als würde ich den Code besser umschreiben, um die Verwendung von Diktaten zu vermeiden - oder eine schnellere Datenstruktur verwenden, die den gleichen Job ausführen kann. Vielen Dank für die Antwort!
Joern
56

Anscheinend ist dict.copy schneller, wie Sie sagen.

[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
quelle
Danke für den Vergleich! Versucht, den Code neu zu schreiben, um die Verwendung von Diktatkopien an den meisten Stellen zu vermeiden. Danke noch einmal!
Joern
4
Die Art und Weise den letzten Vergleich zu tun , ohne die Kosten des den Import zu zählen jedes Mal mit ist timeit‚s -sArgument: python -m timeit -s "from copy import copy" "new = copy({1:1, 2:2, 3:3})". Wenn Sie schon dabei sind, ziehen Sie auch die Diktat-Erstellung heraus (für alle Beispiele)
Thomas Wouters
Vielleicht ist es besser, die Vorgänge viele Male zu wiederholen, da es bei einem bestimmten Schuss zu Schwankungen kommen kann.
Xiaohan2012
2
Timeit macht das; wie es heißt, es schleift 1000000 mal und mittelt es.
utdemir
Ich habe widersprüchliche Zeiten. a = {b: b für b im Bereich (10000)} In [5]:% timeit copy (a) 10000 Schleifen, am besten 3: 186 µs pro Schleife In [6]:% timeit deepcopy (a) 100 Schleifen, Best of 3: 14,1 ms pro Schleife In [7]:% timeit a.copy () 1000 Schleifen, Best of 3: 180 µs pro Schleife
Davoud Taghawi-Nejad
12

Können Sie ein Codebeispiel bereitstellen, damit ich sehen kann, wie Sie copy () verwenden und in welchem ​​Kontext?

Du könntest benutzen

new = dict(old)

Aber ich glaube nicht, dass es schneller gehen wird.

Mike Vaughan
quelle
5

Mir ist klar, dass dies ein alter Thread ist, aber dies ist ein hohes Ergebnis in Suchmaschinen für "Diktierkopie-Python" und das Top-Ergebnis für "Diktierkopie-Leistung", und ich glaube, dass dies relevant ist.

Ab Python 3.7 newDict = oldDict.copy()ist es bis zu 5.5x schneller als zuvor. Insbesondere newDict = dict(oldDict)scheint diese Leistungssteigerung derzeit nicht zu verzeichnen.

Es gibt ein wenig mehr Informationen hier .

iandioch
quelle
3

Abhängig von den Dingen, die Sie der Spekulation überlassen, möchten Sie möglicherweise das ursprüngliche Wörterbuch einpacken und eine Art von Wörterbuch erstellen Copy-on-Write durchführen.

Die "Kopie" ist dann ein Wörterbuch, das Dinge im "übergeordneten" Wörterbuch nachschlägt, wenn es nicht bereits den Schlüssel enthält - sondern Änderungen an sich.

Dies setzt voraus, dass Sie das Original nicht ändern und dass die zusätzlichen Suchvorgänge nicht mehr kosten.

Alex Brasetvik
quelle
2

Die Messungen hängen jedoch von der Wörterbuchgröße ab. Für 10000 Einträge sind copy (d) und d.copy () fast gleich.

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
quelle