Wie kann ich eine Kopie eines Objekts in Python erstellen?
Wenn ich also die Werte der Felder des neuen Objekts ändere, sollte das alte Objekt davon nicht betroffen sein.
Du meinst dann ein veränderliches Objekt.
In Python 3 erhalten Listen eine copy
Methode (in 2 würden Sie ein Slice verwenden, um eine Kopie zu erstellen):
>>> a_list = list('abc')
>>> a_copy_of_a_list = a_list.copy()
>>> a_copy_of_a_list is a_list
False
>>> a_copy_of_a_list == a_list
True
Flache Kopien
Flache Kopien sind nur Kopien des äußersten Behälters.
list.copy
ist eine flache Kopie:
>>> list_of_dict_of_set = [{'foo': set('abc')}]
>>> lodos_copy = list_of_dict_of_set.copy()
>>> lodos_copy[0]['foo'].pop()
'c'
>>> lodos_copy
[{'foo': {'b', 'a'}}]
>>> list_of_dict_of_set
[{'foo': {'b', 'a'}}]
Sie erhalten keine Kopie der Innenobjekte. Sie sind dasselbe Objekt. Wenn sie also mutiert sind, wird die Änderung in beiden Containern angezeigt.
Tiefe Kopien
Tiefe Kopien sind rekursive Kopien jedes inneren Objekts.
>>> lodos_deep_copy = copy.deepcopy(list_of_dict_of_set)
>>> lodos_deep_copy[0]['foo'].add('c')
>>> lodos_deep_copy
[{'foo': {'c', 'b', 'a'}}]
>>> list_of_dict_of_set
[{'foo': {'b', 'a'}}]
Änderungen werden nicht im Original, sondern nur in der Kopie berücksichtigt.
Unveränderliche Gegenstände
Unveränderliche Objekte müssen normalerweise nicht kopiert werden. Wenn Sie es versuchen, gibt Python Ihnen nur das ursprüngliche Objekt:
>>> a_tuple = tuple('abc')
>>> tuple_copy_attempt = a_tuple.copy()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'copy'
Tupel haben nicht einmal eine Kopiermethode, also versuchen wir es mit einem Slice:
>>> tuple_copy_attempt = a_tuple[:]
Aber wir sehen, es ist das gleiche Objekt:
>>> tuple_copy_attempt is a_tuple
True
Ähnliches gilt für Strings:
>>> s = 'abc'
>>> s0 = s[:]
>>> s == s0
True
>>> s is s0
True
und für Frozensets, obwohl sie eine copy
Methode haben:
>>> a_frozenset = frozenset('abc')
>>> frozenset_copy_attempt = a_frozenset.copy()
>>> frozenset_copy_attempt is a_frozenset
True
Wann unveränderliche Objekte kopiert werden sollen
Unveränderliche Objekte sollten kopiert werden, wenn ein veränderbares inneres Objekt kopiert werden muss.
>>> tuple_of_list = [],
>>> copy_of_tuple_of_list = tuple_of_list[:]
>>> copy_of_tuple_of_list[0].append('a')
>>> copy_of_tuple_of_list
(['a'],)
>>> tuple_of_list
(['a'],)
>>> deepcopy_of_tuple_of_list = copy.deepcopy(tuple_of_list)
>>> deepcopy_of_tuple_of_list[0].append('b')
>>> deepcopy_of_tuple_of_list
(['a', 'b'],)
>>> tuple_of_list
(['a'],)
Wie wir sehen können, ändert sich das Original nicht , wenn das innere Objekt der Kopie mutiert ist .
Benutzerdefinierte Objekte
Benutzerdefinierte Objekte speichern Daten normalerweise in einem __dict__
Attribut oder in __slots__
(einer tupelartigen Speicherstruktur).
Um ein kopierbares Objekt zu erstellen, definieren Sie __copy__
(für flache Kopien) und / oder __deepcopy__
(für tiefe Kopien).
from copy import copy, deepcopy
class Copyable:
__slots__ = 'a', '__dict__'
def __init__(self, a, b):
self.a, self.b = a, b
def __copy__(self):
return type(self)(self.a, self.b)
def __deepcopy__(self, memo): # memo is a dict of id's to copies
id_self = id(self) # memoization avoids unnecesary recursion
_copy = memo.get(id_self)
if _copy is None:
_copy = type(self)(
deepcopy(self.a, memo),
deepcopy(self.b, memo))
memo[id_self] = _copy
return _copy
Beachten Sie, dass deepcopy
ein Memo-Wörterbuch von id(original)
(oder Identitätsnummern) Kopien enthält. Um ein gutes Verhalten bei rekursiven Datenstrukturen zu erzielen, stellen Sie sicher, dass Sie noch keine Kopie erstellt haben, und geben Sie diese gegebenenfalls zurück.
Machen wir also ein Objekt:
>>> c1 = Copyable(1, [2])
Und copy
macht eine flache Kopie:
>>> c2 = copy(c1)
>>> c1 is c2
False
>>> c2.b.append(3)
>>> c1.b
[2, 3]
Und deepcopy
jetzt macht eine tiefe Kopie:
>>> c3 = deepcopy(c1)
>>> c3.b.append(4)
>>> c1.b
[2, 3]