2017-02-21 1 views
7

Ein Objekt meiner Klasse hat eine Liste als Attribut. Das heißt,Wenn ich eine nicht-rekursive tiefe Kopie meines Objekts haben möchte, sollte ich in Python copy oder decopy überschreiben?

class T(object): 
    def __init__(self, x, y): 
     self.arr = [x, y] 

Wenn dieses Objekt kopiert wird, möchte ich eine separate Liste arr, aber eine flache Kopie des Inhalts der Liste (z x und y). Daher entscheide ich mich, eine eigene Kopiermethode zu implementieren, die die Liste, aber nicht die darin enthaltenen Elemente neu erstellt. Aber sollte ich das __copy__() oder __deepcopy__() nennen? Welcher ist der richtige Name für das, was ich mache, laut Pythons Semantik?

Meine Schätzung ist __copy__(). Wenn ich deepcopy() rufe, würde ich erwarten, dass der Klon vollständig vom Original entkoppelt ist. Allerdings sagt die documentation:

A tiefe Kopie ein neue Verbindung Objekt konstruiert und dann rekursiv Einsätze Kopien hinein der Objekte im Original gefunden.

Es ist verwirrend mit den Worten „fügt Kopien hinein“ statt „Einsätze tiefer Kopien hinein“, vor allem mit dem Schwerpunkt.

+0

Das Konzept von "tief" kommt aus dem rekursiven Teil, sonst wäre eine rekursive Beschreibung "Eine tiefe Kopie erstellt ein neues zusammengesetztes Objekt und fügt dann tiefe Kopien hinein". –

Antwort

5

Die korrekte magische Methode, die Sie hier implementieren können, ist __copy__.

Das von Ihnen beschriebene Verhalten ist tiefer als das Standardverhalten von copy (d. H. Für ein Objekt, das __copy__ nicht implementiert hat), aber es ist nicht tief genug, um als deepcopy bezeichnet zu werden. Daher sollten Sie __copy__ implementieren, um das gewünschte Verhalten zu erhalten.

Sie machen den Fehler, zu denken nicht, die einfach einen anderen Namen eine „Kopie“ macht die Zuordnung:

t1 = T('google.com', 123) 
t2 = t1 # this does *not* use __copy__ 

Das ist nur ein anderer Name auf der gleichen Instanz bindet. Vielmehr wird die __copy__ Verfahren durch eine Funktion eingehakt:

import copy 
t2 = copy.copy(t1) 
+0

Danke! Ich dachte dasselbe, fühlte mich aber zweifelhaft, als ich die Python-Dokumentation las. Warum macht Python das 'copy' nicht zu einem eingebauten wie' str' und 'repr'? – user31039

+0

Weil es nicht viele Anwendungsfälle dafür gibt. Python-Entwickler arbeiten im Allgemeinen in einem Stil, der Nebenwirkungen vermeidet, so dass sie sich nicht wirklich darum kümmern, wo sich ein Objekt im Speicher befindet. – wim

+0

@ user31039 Weil die meisten Builtins einfach kopiert werden können. Zum Beispiel kann eine Instanz an den Konstruktor übergeben werden ('dict ({'a': 1})' erstellt eine Kopie) oder eine explizite 'copy' Methode:' {1,2,3} .copy() 'or habe sogar eine Slicing-Syntax, um eine Kopie zu erstellen: '[1,2,3] [:]'. Für normale Python-Programme, die die eingebauten Programme verwenden, brauchen Sie das Kopiermodul einfach nicht. Werbung: Ich habe einige dieser Punkte in meiner Antwort berührt. :) – MSeifert

1

I __copy__ denken Überschreiben ist eine gute Idee, wie die anderen Antworten mit sehr ausführlich erklärt. Eine alternative Lösung könnte sein, eine explizite Kopiermethode zu schreiben absolut klar sein, was heißt:

class T(object): 
    def __init__(self, x, y): 
     self.arr = [x, y] 

    def copy(self): 
     return T(*self.arr) 

Wenn jeder künftige Leser t.copy() sieht er dann sofort weiß, dass eine benutzerdefinierte Kopiermethode implementiert wurde. Der Nachteil ist natürlich, dass es nicht gut mit Bibliotheken von Drittanbietern spielen kann, die copy.copy(t) verwenden.

+0

so Ihre Antwort auf die Frage "sollte ich Kopie oder Deepcopy außer Kraft setzen?" aus dem Titel ist .... weder und vergessen Sie 'copy.copy' alle zusammen? –

+0

Es ist nur eine alternative Idee. Ich denke, es hat einige Vorteile, aber ich bin mir auch der negativen Auswirkungen voll bewusst. –

+2

ok außer, wenn '__copy__' definiert ist, wird es aufgerufen, wenn Sie 'copy.copy (t)' ausführen. Es scheint mir immer noch, dass Sie nicht wirklich eine Lösung für die gestellte Frage anbieten. (Ich nehme an, Downvoter war aus diesem Grund) –

3

Es kommt tatsächlich auf das gewünschte Verhalten Ihrer Klasse an, das in die Entscheidung eingreift, was überschrieben werden soll (__copy__ oder __deepcopy__).

Im Allgemeinen copy.deepcopy arbeitet meist richtig, es ist einfach alles Kopien (rekursiv), so müssen Sie nur es außer Kraft zu setzen, wenn es eine Eigenschaft ist, dass darf nicht kopiert werden (je!).

Auf der anderen Seite sollte man __copy__ nur dann definieren, wenn Benutzer (einschließlich Sie selbst) nicht erwarten würden, dass Änderungen an kopierte Instanzen weitergegeben werden. Zum Beispiel, wenn Sie einfach einen veränderbaren Typ (wie list) umbrechen oder änderbare Typen als Implementierungsdetails verwenden.

Dann gibt es auch den Fall, dass der minimale Satz von zu kopierenden Attributen nicht klar definiert ist. In diesem Fall würde ich auch __copy__ außer Kraft setzen, aber möglicherweise eine TypeError dort erhöhen und möglicherweise eine (oder mehrere) dedizierte öffentliche copy Methoden einschließen.

jedoch meiner Meinung nach die arr zählt als Detail Umsetzung und somit würde ich __copy__ außer Kraft setzen:

class T(object): 
    def __init__(self, x, y): 
     self.arr = [x, y] 

    def __copy__(self): 
     new = self.__class__(*self.arr) 
     # ... maybe other stuff 
     return new 

einfach, es zu zeigen wie erwartet funktioniert:

from copy import copy, deepcopy 

x = T([2], [3]) 
y = copy(x) 
x.arr is y.arr  # False 
x.arr[0] is y.arr[0] # True 
x.arr[1] is y.arr[1] # True 

x = T([2], [3]) 
y = deepcopy(x) 
x.arr is y.arr  # False 
x.arr[0] is y.arr[0] # False 
x.arr[1] is y.arr[1] # False 

Nur eine kurze Notiz über Erwartungen:

Benutzer erwarten im Allgemeinen t Außerdem können Sie eine Instanz an den Konstruktor übergeben, um eine minimale Kopie (ähnlich oder identisch mit __copy__) zu erstellen. Zum Beispiel:

lst1 = [1,2,3,4] 
lst2 = list(lst1) 
lst1 is lst2  # False 

Einiger Python-Typen haben ein expliziten copy Verfahren, dass (falls vorhanden) als __copy__ das gleiche tun. Das würde erlauben ausdrücklich in Parameter übergeben (allerdings habe ich das noch nicht in Aktion gesehen):

lst3 = lst1.copy() # python 3.x only (probably) 
lst3 is lst1  # False 

Wenn Ihre Klasse von anderen verwendet werden, sollten Sie wahrscheinlich brauchen diese Punkte zu berücksichtigen, aber wenn Sie nur wollen Machen Sie Ihre Klasse mit copy.copy arbeiten dann überschreiben Sie einfach __copy__.

Verwandte Themen