Ich verstehe den Unterschied zwischen copy
vs. deepcopy
im Kopiermodul. Ich habe copy.copy
und copy.deepcopy
zuvor erfolgreich verwendet, aber dies ist das erste Mal, dass ich die __copy__
und __deepcopy__
Methoden überladen habe . Ich habe schon gegoogelt um und sah durch die integrierten Python - Module für Instanzen des aussehen __copy__
und __deepcopy__
Funktionen (zB sets.py
, decimal.py
und fractions.py
), aber ich bin immer noch nicht zu 100% sicher , dass ich es richtig habe.
Hier ist mein Szenario:
Ich habe ein Konfigurationsobjekt. Zunächst werde ich ein Konfigurationsobjekt mit einem Standardwertsatz instanziieren. Diese Konfiguration wird an mehrere andere Objekte übergeben (um sicherzustellen, dass alle Objekte mit derselben Konfiguration beginnen). Sobald die Benutzerinteraktion beginnt, muss jedes Objekt seine Konfigurationen unabhängig voneinander anpassen, ohne die Konfigurationen des anderen zu beeinflussen (was bedeutet, dass ich meine ursprüngliche Konfiguration gründlich kopieren muss, um sie weiterzugeben).
Hier ist ein Beispielobjekt:
class ChartConfig(object):
def __init__(self):
#Drawing properties (Booleans/strings)
self.antialiased = None
self.plot_style = None
self.plot_title = None
self.autoscale = None
#X axis properties (strings/ints)
self.xaxis_title = None
self.xaxis_tick_rotation = None
self.xaxis_tick_align = None
#Y axis properties (strings/ints)
self.yaxis_title = None
self.yaxis_tick_rotation = None
self.yaxis_tick_align = None
#A list of non-primitive objects
self.trace_configs = []
def __copy__(self):
pass
def __deepcopy__(self, memo):
pass
Was ist der richtige Weg, um die copy
und deepcopy
Methoden für dieses Objekt zu implementieren, um sicherzustellen copy.copy
und copy.deepcopy
mir das richtige Verhalten zu geben?
quelle
Antworten:
Die Empfehlungen zum Anpassen finden Sie ganz am Ende der Dokumentenseite :
Da Sie sich anscheinend nicht um die Anpassung von Beizen kümmern, scheint das Definieren
__copy__
und__deepcopy__
definitiv der richtige Weg für Sie zu sein.Insbesondere
__copy__
(die flache Kopie) ist in Ihrem Fall ziemlich einfach ...:__deepcopy__
wäre ähnlich (würde auch einmemo
Argument akzeptieren ), aber vor der Rückgabe müssteself.foo = deepcopy(self.foo, memo)
jedes Attribut aufgerufen werden,self.foo
das gründlich kopiert werden muss (im Wesentlichen Attribute, die Container sind - Listen, Diktate, nicht-primitive Objekte, die andere Dinge durch ihre__dict__
s enthalten).quelle
__copy__
/ zu verwenden__deepcopy__
.self.foo = deepcopy(self.foo, memo)
...? Meinst du nicht wirklichnewone.foo = ...
?__init__
. Das macht die Kopie nicht. Es gibt auch sehr oft einen Anwendungsfall, in dem Beizen und Kopieren unterschiedlich sein müssen. Tatsächlich weiß ich nicht einmal, warum copy standardmäßig versucht, das Beizprotokoll zu verwenden. Das Kopieren dient der In-Memory-Manipulation, das Beizen der epochenübergreifenden Persistenz. es sind ganz andere Dinge, die wenig miteinander zu tun haben.Wenn Sie die Antwort von Alex Martelli und den Kommentar von Rob Young zusammenfassen, erhalten Sie den folgenden Code:
druckt
Hier wird
__deepcopy__
dasmemo
Diktat ausgefüllt, um übermäßiges Kopieren zu vermeiden, falls auf das Objekt selbst von seinem Mitglied verwiesen wird.quelle
Transporter
?Transporter
ist der Name meiner Klasse, die ich schreibe. Für diese Klasse möchte ich das Deepcopy-Verhalten überschreiben.Transporter
?__deepcopy__
sollte einen Test enthalten, um eine unendliche Rekursion zu vermeiden: <! - Sprache: lang-python -> d = id (self) result = memo.get (d, None), wenn das Ergebnis nicht None ist: Ergebnis zurückgebenmemo[id(self)]
tatsächlich verwendet wird, um eine unendliche Rekursion zu verhindern. Ich habe ein kurzes Beispiel zusammengestellt, das darauf hinweist, dasscopy.deepcopy()
der Aufruf eines Objekts intern abgebrochen wird, wennid()
es ein Schlüssel von istmemo
, richtig? Es ist auch erwähnenswert, dassdeepcopy()
dies standardmäßig von selbst zu tun scheint , was es schwierig macht, sich einen Fall vorzustellen, in dem eine__deepcopy__
manuelle Definition tatsächlich erforderlich ist ...Nach Peters hervorragender Antwort , um eine benutzerdefinierte Deepcopy mit minimalen Änderungen an der Standardimplementierung zu implementieren (z. B. nur ein Feld so zu ändern, wie ich es brauchte):
quelle
delattr(self, '__deepcopy__')
dann vorzuziehensetattr(self, '__deepcopy__', deepcopy_method)
?Aus Ihrem Problem geht nicht hervor, warum Sie diese Methoden überschreiben müssen, da Sie die Kopiermethoden nicht anpassen möchten.
Wenn Sie die Deep Copy anpassen möchten (z. B. indem Sie einige Attribute freigeben und andere kopieren), finden Sie hier eine Lösung:
quelle
__deepcopy__
Methode zurücksetzen, da er__deepcopy__
= Keine hat?__deepcopy__
Methode nicht gefunden wird (oderobj.__deepcopy__
None zurückgibt), wirddeepcopy
auf die Standard-Tiefkopierfunktion zurückgegriffen. Dies kann hier gesehen werden__deepcopy__=None
Attribut aus dem Klon gelöscht habe . Siehe neuen Code.Ich bin vielleicht ein bisschen daneben, aber hier geht es weiter.
Aus den
copy
Dokumenten ;Mit anderen Worten:
copy()
Kopiert nur das oberste Element und belässt den Rest als Zeiger in der ursprünglichen Struktur.deepcopy()
wird rekursiv über alles kopieren.Das ist,
deepcopy()
was Sie brauchen.Wenn Sie etwas wirklich Spezifisches tun müssen, können Sie es überschreiben
__copy__()
oder__deepcopy__()
, wie im Handbuch beschrieben. Persönlich würde ich wahrscheinlich eine einfache Funktion (z. B.config.copy_config()
oder so) implementieren , um deutlich zu machen, dass es sich nicht um Python-Standardverhalten handelt.quelle
__copy__(
) und__deepcopy__()
. docs.python.org/library/copy.htmlDas
copy
Modul verwendet schließlich das__getstate__()
/ pickling-Protokoll , daher sind dies auch gültige Ziele, die überschrieben werden müssen.__setstate__()
Die Standardimplementierung gerade Rückkehr und setzt die
__dict__
von der Klasse, so dass Sie müssen nicht nennensuper()
cleveren Trick und Sorgen über Eino Gourdin der, oben .quelle
Aufbauend auf der klaren Antwort von Antony Hatchkins ist hier meine Version, in der die betreffende Klasse von einer anderen benutzerdefinierten Klasse stammt (st müssen wir anrufen
super
):quelle