¿Cuál es la diferencia entre copia superficial, copia profunda y operación de asignación normal?

Resuelto deeshank asked hace 11 años • 13 respuestas
import copy

a = "deepak"
b = 1, 2, 3, 4
c = [1, 2, 3, 4]
d = {1: 10, 2: 20, 3: 30}

a1 = copy.copy(a)
b1 = copy.copy(b)
c1 = copy.copy(c)
d1 = copy.copy(d)


print("immutable - id(a)==id(a1)", id(a) == id(a1))
print("immutable - id(b)==id(b1)", id(b) == id(b1))
print("mutable - id(c)==id(c1)", id(c) == id(c1))
print("mutable - id(d)==id(d1)", id(d) == id(d1))

Obtengo los siguientes resultados:

immutable - id(a)==id(a1) True
immutable - id(b)==id(b1) True
mutable - id(c)==id(c1) False
mutable - id(d)==id(d1) False

Si realizo una copia profunda:

a1 = copy.deepcopy(a)
b1 = copy.deepcopy(b)
c1 = copy.deepcopy(c)
d1 = copy.deepcopy(d)

los resultados son los mismos:

immutable - id(a)==id(a1) True
immutable - id(b)==id(b1) True
mutable - id(c)==id(c1) False
mutable - id(d)==id(d1) False

Si trabajo en operaciones de asignación:

a1 = a
b1 = b
c1 = c
d1 = d

entonces los resultados son:

immutable - id(a)==id(a1) True
immutable - id(b)==id(b1) True
mutable - id(c)==id(c1) True
mutable - id(d)==id(d1) True

¿Alguien puede explicar qué marca exactamente la diferencia entre las copias? ¿Es algo relacionado con objetos mutables e inmutables? Si es así, ¿podrías explicármelo?

deeshank avatar Jun 22 '13 09:06 deeshank
Aceptado

Las operaciones de asignación normales simplemente apuntarán la nueva variable hacia el objeto existente. Los documentos explican la diferencia entre copias superficiales y profundas:

La diferencia entre copia superficial y profunda solo es relevante para objetos compuestos (objetos que contienen otros objetos, como listas o instancias de clase):

  • Una copia superficial construye un nuevo objeto compuesto y luego (en la medida de lo posible) inserta referencias a los objetos encontrados en el original.

  • Una copia profunda construye un nuevo objeto compuesto y luego, de forma recursiva, inserta copias de los objetos encontrados en el original.

Aquí hay una pequeña demostración:

import copy

a = [1, 2, 3]
b = [4, 5, 6]
c = [a, b]

Usando operaciones de asignación normales para copiar:

d = c

print id(c) == id(d)          # True - d is the same object as c
print id(c[0]) == id(d[0])    # True - d[0] is the same object as c[0]

Usando una copia superficial:

d = copy.copy(c)

print id(c) == id(d)          # False - d is now a new object
print id(c[0]) == id(d[0])    # True - d[0] is the same object as c[0]

Usando una copia profunda:

d = copy.deepcopy(c)

print id(c) == id(d)          # False - d is now a new object
print id(c[0]) == id(d[0])    # False - d[0] is now a new object
grc avatar Jun 22 '2013 02:06 grc

Para objetos inmutables, no es necesario copiarlos porque los datos nunca cambiarán, por lo que Python usa los mismos datos; Los identificadores son siempre los mismos. Para objetos mutables, dado que potencialmente pueden cambiar, la copia [superficial] crea un nuevo objeto.

La copia profunda está relacionada con estructuras anidadas. Si tiene una lista de listas, copie en profundidad copiestambién las listas anidadas, por lo que es una copia recursiva. Con solo copiar, tiene una nueva lista externa, pero las listas internas son referencias.

La tarea no se copia. Simplemente establece la referencia a los datos antiguos. Entonces necesitas copiar para crear una nueva lista con el mismo contenido.

perreal avatar Jun 22 '2013 02:06 perreal

Para objetos inmutables, crear una copia no tiene mucho sentido ya que no van a cambiar. Para objetos mutables, assignmenty copyse deepcopycomportan de manera diferente. Hablemos de cada uno de ellos con ejemplos.

Una operación de asignación simplemente asigna la referencia del origen al destino, por ejemplo:

>>> i = [1,2,3]
>>> j=i
>>> hex(id(i)), hex(id(j))
>>> ('0x10296f908', '0x10296f908') #Both addresses are identical

Ahora iy jtécnicamente nos referimos a la misma lista. Ambos iy jtienen la misma dirección de memoria. Cualquier actualización de uno de ellos se reflejará en el otro, por ejemplo:

>>> i.append(4)
>>> j
>>> [1,2,3,4] #Destination is updated

>>> j.append(5)
>>> i
>>> [1,2,3,4,5] #Source is updated

Por otro lado, copyy deepcopycrea una nueva copia de la variable. Entonces, ahora los cambios en la variable original no se reflejarán en la variable de copia y viceversa. Sin embargo, copy( copia superficial ) no crea una copia de los objetos anidados, sino que simplemente copia las referencias a los objetos anidados, mientras que deepcopy( copia profunda ) copia todos los objetos anidados de forma recursiva.

Algunos ejemplos para demostrar el comportamiento de copyy deepcopy:

Ejemplo de lista plana usando copy:

>>> import copy
>>> i = [1,2,3]
>>> j = copy.copy(i)
>>> hex(id(i)), hex(id(j))
>>> ('0x102b9b7c8', '0x102971cc8') #Both addresses are different

>>> i.append(4)
>>> j
>>> [1,2,3] #Update of original list didn't affect the copied variable

Ejemplo de lista anidada usando copy:

>>> import copy
>>> i = [1,2,3,[4,5]]
>>> j = copy.copy(i)

>>> hex(id(i)), hex(id(j))
>>> ('0x102b9b7c8', '0x102971cc8') #Both addresses are still different

>>> hex(id(i[3])), hex(id(j[3]))
>>> ('0x10296f908', '0x10296f908') #Nested lists have the same address

>>> i[3].append(6)
>>> j
>>> [1,2,3,[4,5,6]] #Update of original nested list updated the copy as well

Ejemplo de lista plana usando deepcopy:

>>> import copy
>>> i = [1,2,3]
>>> j = copy.deepcopy(i)
>>> hex(id(i)), hex(id(j))
>>> ('0x102b9b7c8', '0x102971cc8') #Both addresses are different

>>> i.append(4)
>>> j
>>> [1,2,3] #Update of original list didn't affect the copied variable

Ejemplo de lista anidada usando deepcopy:

>>> import copy
>>> i = [1,2,3,[4,5]]
>>> j = copy.deepcopy(i)

>>> hex(id(i)), hex(id(j))
>>> ('0x102b9b7c8', '0x102971cc8') #Both addresses are still different

>>> hex(id(i[3])), hex(id(j[3]))
>>> ('0x10296f908', '0x102b9b7c8') #Nested lists have different addresses

>>> i[3].append(6)
>>> j
>>> [1,2,3,[4,5]] #Update of original nested list didn't affect the copied variable    
Sohaib Farooqi avatar Oct 22 '2017 15:10 Sohaib Farooqi

Veamos en un ejemplo gráfico cómo se ejecuta el siguiente código:

import copy

class Foo(object):
    def __init__(self):
        pass


a = [Foo(), Foo()]
shallow = copy.copy(a)
deep = copy.deepcopy(a)

ingrese la descripción de la imagen aquí

user1767754 avatar Jun 08 '2017 21:06 user1767754