Frage

Ich habe eine Baumstruktur der Widgets -EG -Sammlung enthält Modelle und Modell enthält Widgets. Ich möchte die ganze Sammlung kopieren, copy.deepcopy ist im Vergleich zu 'Gurke und Depickle' das Objekt schneller, aber cpickle als in C geschrieben ist viel schneller, also ist es viel schneller

  1. Warum sollte ich (wir) Cpickle nicht immer anstelle von DeepCopy verwenden?
  2. Gibt es eine andere Kopienalternative? Weil die Gurke langsamer ist als DeepCopy, aber Cpickle ist schneller. Eine C -Implementierung von DeepCopy ist also der Gewinner.

Beispiel -Testcode:

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))

Timings:

>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
War es hilfreich?

Lösung

Das Problem ist, Pickle+Unpickle kann (in der C -Implementierung) schneller sein, weil es ist Weniger allgemein als DeepCopy: Viele Objekte können tiefen, aber nicht eingelegt werden. Nehmen wir zum Beispiel an, dass Ihre Klasse A wurden geändert in ...:

class A(object):
  class B(object): pass
  def __init__(self): self.b = self.B()

jetzt, copy1 Funktioniert immer noch gut (die Komplexität von A verlangsamt es jedoch, aber es hält es absolut nicht auf); copy2 und copy3 Break, das Ende der Stapelspur sagt ...:

  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

IE, Pickling geht immer davon aus, dass Kurse und Funktionen in ihren Modulen auf höchstem Niveau Entitäten sind und sie "mit dem Namen" so einwickelt-DeepCoping macht absolut keine solchen Annahmen.

Wenn Sie also eine Situation haben, in der die Geschwindigkeit von "etwas tiefem Kopieren" absolut von entscheidender Bedeutung ist, ist jede Millisekundenwerte wichtig, und Sie möchten die besonderen Einschränkungen nutzen, von denen Sie wissen, dass sie für die von Ihnen doppelten Objekte gelten, z. anwendbar oder diejenigen, die andere Formen noch von Serialisierungen und anderen Verknüpfungen bevorzugen, machen Sie auf jeden Fall weiter - aber wenn Sie dies tun Explizit zum Nutzen zukünftiger Betreuer.

Für den normalen Fall, in dem Sie Allgemeinheit wünschen, verwenden Sie deepcopy!-)

Andere Tipps

Sie sollten DeepCopy verwenden, da Ihr Code lesbarer wird. Die Verwendung eines Serialisierungsmechanismus zum Kopieren von Objekten im Speicher ist zumindest für einen anderen Entwickler, der Ihren Code liest, zumindest verwirrend. Die Verwendung von DeepCopy bedeutet auch, dass Sie die Vorteile künftiger Optimierungen in DeepCopy nutzen können.

Erste Regel der Optimierung: Nicht.

es ist nicht Immer der Fall, dass CPICKLE schneller ist als DeepCopy (). Während Cpickle wahrscheinlich immer schneller als Gurke ist, hängt es schneller als DeepCopy ab, von dem es abhängt

  • die Größe und Verschachtelung der zu kopierten Strukturen,
  • die Art der enthaltenen Objekte und
  • die Größe der eingelegten String -Darstellung.

Wenn etwas eingelegt werden kann, kann es offensichtlich tiefe Kopie werden, aber das Gegenteil ist nicht der Fall: Um etwas zu wählen, muss es vollständig serialisiert werden; Dies ist bei DeepCoping nicht der Fall. Insbesondere können Sie implementieren __deepcopy__ Sehr effizient durch Kopieren einer Struktur im Speicher (denken Sie an Erweiterungstypen), ohne in der Lage zu sein, alles auf der Festplatte zu speichern. (Denken Sie an Suspend-to-Ram vs. Suspend-to-Tisk.)

Ein bekannter Erweiterungstyp, der die obigen Bedingungen erfüllt ndarray, und in der Tat dient es als gutes Gegenbeispiel für Ihre Beobachtung: mit d = numpy.arange(100000000), Ihr Code gibt unterschiedliche Laufzeiten:

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

Wenn __deepcopy__ wird nicht implementiert, copy und pickle gemeinsame Infrastruktur teilen (vgl. copy_reg Modul, diskutiert in Beziehung zwischen Gurke und DeepCopy).

Noch schneller wäre es, die Kopie überhaupt zu vermeiden. Sie erwähnen, dass Sie Rendering machen. Warum muss es Objekte kopieren?

Kurz und etwas spät:

  • Wenn Sie ein Objekt trotzdem cpickle müssen, können Sie die CPICKLE -Methode auch für DeepCopy verwenden (aber Dokument).

zB Sie könnten in Betracht ziehen:

def mydeepcopy(obj):
    try:
       return cPickle.loads(cPickle.dumps(obj, -1))
    except PicklingError:
       return deepcopy(obj)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top