Je voudrais créer une copie d'un objet. Je veux que le nouvel objet possède toutes les propriétés de l'ancien objet (valeurs des champs). Mais je veux avoir des objets indépendants. Donc, si je change les valeurs des champs du nouvel objet, cela n’affectera pas l’ancien objet.
Pour obtenir une copie totalement indépendante d'un objet, vous pouvez utiliser la fonction copy.deepcopy()
.
Pour plus de détails sur la copie superficielle et profonde, veuillez vous reporter aux autres réponses à cette question et à l'explication de Nice dans cette réponse à une question connexe .
Comment créer une copie d'un objet en Python?
Donc, si je change les valeurs des champs du nouvel objet, cela n’affectera pas l’ancien objet.
Vous voulez dire un objet mutable alors.
Dans Python 3, les listes obtiennent une méthode copy
(dans 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
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'obtenez pas une copie des objets intérieurs. Ils sont le même objet - donc, quand ils sont mutés, le changement apparaît dans les deux conteneurs.
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.
Les objets immuables n'ont généralement pas besoin d'être copiés. En fait, si vous essayez, 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 n-uplets n'ont même pas de méthode de copie, alors essayons-le avec une tranche:
>>> 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 chaînes:
>>> s = 'abc'
>>> s0 = s[:]
>>> s == s0
True
>>> s is s0
True
et pour frozensets, même s'ils ont une méthode copy
:
>>> a_frozenset = frozenset('abc')
>>> frozenset_copy_attempt = a_frozenset.copy()
>>> frozenset_copy_attempt is a_frozenset
True
Les objets immuables devraient être copiés 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 constater, lorsque l'objet intérieur de la copie est muté, l'original change not.
Les objets personnalisés stockent généralement les données dans un attribut __dict__
ou dans __slots__
(une structure de mémoire de type Tuple).
Pour créer un objet pouvant être copié, 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émoization de id(original)
(ou numéros d’identité) sur les copies. Pour profiter du bon comportement avec les structures de données récursives, assurez-vous de ne pas avoir déjà fait de copie et, le cas échéant, renvoyez-le.
Alors faisons un objet:
>>> c1 = Copyable(1, [2])
Et copy
en 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 en profondeur:
>>> c3 = deepcopy(c1)
>>> c3.b.append(4)
>>> c1.b
[2, 3]
Copie peu profonde avec copy.copy()
#!/usr/bin/env python3
import copy
class C():
def __init__(self):
self.x = [1]
self.y = [2]
# It copies.
c = C()
d = copy.copy(c)
d.x = [3]
assert c.x == [1]
assert d.x == [3]
# It's shallow.
c = C()
d = copy.copy(c)
d.x[0] = 3
assert c.x == [3]
assert d.x == [3]
Copie profonde avec copy.deepcopy()
#!/usr/bin/env python3
import copy
class C():
def __init__(self):
self.x = [1]
self.y = [2]
c = C()
d = copy.deepcopy(c)
d.x[0] = 3
assert c.x == [1]
assert d.x == [3]
Documentation: https://docs.python.org/3/library/copy.html
Testé sur Python 3.6.5.