Saltar al contenido

¿Cómo puedo crear una copia de un objeto en Python?

Posteriormente a consultar con especialistas en el tema, programadores de varias ramas y maestros dimos con la solución a la cuestión y la plasmamos en esta publicación.

Solución:

Para obtener una copia totalmente independiente de un objeto, puede utilizar el copy.deepcopy() función.

Para obtener más detalles sobre la copia superficial y profunda, consulte las otras respuestas a esta pregunta y la buena explicación en esta respuesta a una pregunta relacionada.

¿Cómo puedo crear una copia de un objeto en Python?

Entonces, si cambio los valores de los campos del nuevo objeto, el objeto anterior 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 una porción 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 mutan, 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 no suelen necesitar copiarse. De hecho, si lo intenta, Python simplemente le dará el objeto original:

>>> a_tuple = tuple('abc')
>>> tuple_copy_attempt = a_tuple.copy()
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: 'tuple' object has no attribute 'copy'

Las tuplas ni siquiera tienen un método de copia, así que intenté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 las cadenas:

>>> s = 'abc'
>>> s0 = s[:]
>>> s == s0
True
>>> s is s0
True

y para frozensets, 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

Cuándo copiar objetos inmutables

Objetos inmutables deberían copiarse si necesita copiar un objeto interior 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'],)

Como vemos, cuando se muta el objeto interior de la copia, el original no no cambiar.

Objetos personalizados

Los objetos personalizados suelen almacenar datos en un __dict__ attribute o en __slots__ (una estructura de memoria similar a una 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) a copias. Para disfrutar de un buen comportamiento con las estructuras de datos recursivas, asegúrese de no haber hecho una copia y, si la ha hecho, devuélvala.

Así que 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]

copia superficial con 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]

Copia profunda con 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]

Documentación: https://docs.python.org/3/library/copy.html

Probado en Python 3.6.5.

Si te gusta el asunto, puedes dejar un artículo acerca de qué le añadirías a esta crónica.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *