Comment puis-je créer une copie d'un objet en Python ?
Ainsi, si je modifie les valeurs des champs du nouvel objet, l'ancien objet ne devrait pas en être affecté.
Vous voulez dire un objet mutable alors.
En Python 3, les listes obtiennent un copy
(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 superficielles
Les copies superficielles ne sont que des copies du conteneur le plus externe.
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'obtenez pas une copie des objets intérieurs. Il s'agit du même objet. Ainsi, lorsqu'ils sont mutés, la modification apparaît dans les deux conteneurs.
Copies profondes
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, mais 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 de le faire, Python vous donnera simplement l'objet original :
>>> 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 avec un slice :
>>> tuple_copy_attempt = a_tuple[:]
Mais on voit que c'est le même objet :
>>> tuple_copy_attempt is a_tuple
True
De même pour les cordes :
>>> 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
Objets immuables devrait être copié si vous avez besoin de copier un objet intérieur mutable.
>>> 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 le fait pas. no changement.
Objets personnalisés
Les objets personnalisés stockent généralement les données dans un __dict__
ou dans l'attribut __slots__
(une structure de mémoire de type tuple).
Pour faire un objet copiable, définissez __copy__
(pour les copies superficielles) et/ou __deepcopy__
(pour les copies profondes).
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 de id(original)
(ou numéros d'identité) aux copies. Pour profiter d'un bon comportement avec les structures de données récursives, assurez-vous que vous n'avez pas déjà fait une copie, et si c'est le cas, renvoyez-la.
Donc, faisons 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 profonde :
>>> c3 = deepcopy(c1)
>>> c3.b.append(4)
>>> c1.b
[2, 3]