Comment puis-je créer une copie d'un objet en Python?
Donc, si je change les valeurs des champs du nouvel objet, l'ancien objet ne devrait pas être affecté par cela.
Vous voulez dire un objet mutable alors.
En Python 3, les listes obtiennent une copy
méthode (en 2, vous utiliseriez une tranche pour faire une copie):
>>> 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
Copies peu profondes
Les copies superficielles ne sont que des copies du conteneur le plus à l'extérieur.
list.copy
est une copie superficielle:
>>> 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'}}]
Vous n'avez pas de copie des objets intérieurs. Ils sont le même objet - donc lorsqu'ils sont mutés, le changement apparaît dans les deux conteneurs.
Copies complètes
Les copies profondes sont des copies récursives de chaque objet intérieur.
>>> 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'}}]
Les modifications ne sont pas reflétées dans l'original, uniquement dans la copie.
Objets immuables
Les objets immuables n'ont généralement pas besoin d'être copiés. En fait, si vous essayez, Python ne vous donnera que l'objet d'origine:
>>> 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'
Les tuples n'ont même pas de méthode de copie, alors essayons-le avec une tranche:
>>> tuple_copy_attempt = a_tuple[:]
Mais nous voyons que c'est le même objet:
>>> tuple_copy_attempt is a_tuple
True
De même pour les chaînes:
>>> s = 'abc'
>>> s0 = s[:]
>>> s == s0
True
>>> s is s0
True
et pour les frozensets, même s'ils ont une copy
méthode:
>>> a_frozenset = frozenset('abc')
>>> frozenset_copy_attempt = a_frozenset.copy()
>>> frozenset_copy_attempt is a_frozenset
True
Quand copier des objets immuables
Les objets immuables doivent être copiés si vous avez besoin d'un objet intérieur mutable copié.
>>> 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'],)
Comme nous pouvons le voir, lorsque l'objet intérieur de la copie est muté, l'original ne change pas .
Objets personnalisés
Les objets personnalisés stockent généralement des données dans un __dict__
attribut ou dans __slots__
(une structure de mémoire de type tuple).
Pour créer un objet copiable, définissez __copy__
(pour les copies superficielles) et / ou __deepcopy__
(pour les copies complètes ).
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
Notez que deepcopy
conserve un dictionnaire de mémorisation id(original)
(ou numéros d'identité) pour les copies. Pour profiter d'un bon comportement avec des structures de données récursives, assurez-vous que vous n'en avez pas déjà fait une copie et, si vous en avez, renvoyez-la.
Faisons donc un objet:
>>> c1 = Copyable(1, [2])
Et copy
fait une copie superficielle:
>>> c2 = copy(c1)
>>> c1 is c2
False
>>> c2.b.append(3)
>>> c1.b
[2, 3]
Et deepcopy
fait maintenant une copie complète:
>>> c3 = deepcopy(c1)
>>> c3.b.append(4)
>>> c1.b
[2, 3]