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 copymé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 copymé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 deepcopyconserve 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 copyfait une copie superficielle:
>>> c2 = copy(c1)
>>> c1 is c2
False
>>> c2.b.append(3)
>>> c1.b
[2, 3]
Et deepcopyfait maintenant une copie complète:
>>> c3 = deepcopy(c1)
>>> c3.b.append(4)
>>> c1.b
[2, 3]