¿Cómo puedo crear una copia de un objeto en Python?
Entonces, si cambio los valores de los campos del nuevo objeto, el objeto antiguo no debería verse afectado por eso.
Te refieres a un objeto mutable entonces.
En Python 3, las listas obtienen un copy
método (en 2, usaría un segmento para hacer una copia):
>>> 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
Copias superficiales
Las copias superficiales son solo copias del contenedor más externo.
list.copy
es una copia superficial:
>>> 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'}}]
No obtienes una copia de los objetos interiores. Son el mismo objeto, por lo que cuando están mutados, el cambio se muestra en ambos contenedores.
Copias profundas
Las copias profundas son copias recursivas de cada objeto interior.
>>> 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'}}]
Los cambios no se reflejan en el original, solo en la copia.
Objetos inmutables
Los objetos inmutables generalmente no necesitan copiarse. De hecho, si lo intentas, Python solo te dará el objeto 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'
Las tuplas ni siquiera tienen un método de copia, así que probémoslo con un segmento:
>>> tuple_copy_attempt = a_tuple[:]
Pero vemos que es el mismo objeto:
>>> tuple_copy_attempt is a_tuple
True
Del mismo modo para cadenas:
>>> s = 'abc'
>>> s0 = s[:]
>>> s == s0
True
>>> s is s0
True
y para congelados, a pesar de que tienen un copy
método:
>>> a_frozenset = frozenset('abc')
>>> frozenset_copy_attempt = a_frozenset.copy()
>>> frozenset_copy_attempt is a_frozenset
True
Cuando copiar objetos inmutables
Los objetos inmutables deben copiarse si necesita un objeto interior mutable copiado.
>>> 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'],)
Como podemos ver, cuando el objeto interior de la copia está mutado, el original no cambia.
Objetos personalizados
Los objetos personalizados generalmente almacenan datos en un __dict__
atributo o en __slots__
(una estructura de memoria tipo tupla).
Para hacer un objeto copiable, defina __copy__
(para copias superficiales) y / o __deepcopy__
(para copias profundas).
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
Tenga en cuenta que deepcopy
mantiene un diccionario de memorización de id(original)
(o números de identidad) para las copias. Para disfrutar de un buen comportamiento con estructuras de datos recursivas, asegúrese de que aún no haya hecho una copia y, si la tiene, devuélvala.
Entonces hagamos un objeto:
>>> c1 = Copyable(1, [2])
Y copy
hace una copia superficial:
>>> c2 = copy(c1)
>>> c1 is c2
False
>>> c2.b.append(3)
>>> c1.b
[2, 3]
Y deepcopy
ahora hace una copia profunda:
>>> c3 = deepcopy(c1)
>>> c3.b.append(4)
>>> c1.b
[2, 3]