12.8. Copiado

El uso de alias puede hacer que un programa sea difícil de leer, porque los cambios hechos en un lugar pueden tener efectos inesperados en otro lugar. Es difícil estar al tanto de todas las variables a las que puede apuntar un objeto
dado.

Copiar un objeto es, muchas veces, una alternativa a la creación de un alias. El modulo copy contiene una función llamada copy que puede duplicar cualquier
objeto:

   1: >>> import copy
   2: >>> p1 = Punto()
   3: >>> p1.x = 3
   4: >>> p1.y = 4
   5: >>> p2 = copy.copy(p1)
   6: >>> p1 == p2
   7: 0
   8: >>> mismoPunto(p1, p2)
   9: 1

Una vez que hemos importado el modulo copy, podemos usar el metodo copy para hacer un nuevo Punto. p1 y p2 no son el mismo punto, pero contienen los mismos datos.


Para copiar un objeto simple como un Punto, que no contiene objetos incrustados, copy es suficiente. Esto se llama copiado superficial.


Para algo como un Rectangulo, que contiene una referencia a un Punto, copy no lo hace del todo bien. Copia la referencia al objeto Punto, de modo que tanto el Rectangulo viejo como el nuevo apuntan a un único Punto.


Si creamos una caja, b1, de la forma habitual y entonces hacemos una copia, b2, usando copy, el diagrama de estados resultante se ve así:


Sin título


Es casi seguro que esto no es lo que queremos. En este caso, la invocación de agrandaRect sobre uno de los Rectángulos no afectaría al otro, ¡pero la invocación de mueveRect sobre cualquiera afectaría a ambos! Este comportamiento es confuso y propicia los errores.



Afortunadamente, el modulo copy contiene un metodo llamado deepcopy que copia no solo el objeto sino también cualesquiera objetos incrustados. No le sorprenderá saber que esta operación se llama copia profunda (deep copy).




   1: >>> b2 = copy.deepcopy(b1)

Ahora b1 y b2 son objetos totalmente independientes.


Podemos usar deepcopy para reescribir agrandaRect de modo que en lugar de modificar un Rectangulo existente, cree un nuevo Rectangulo que tiene la misma localización que el viejo pero nuevas dimensiones:




   1: def agrandaRect(caja, danchura, daltura) :
   2:     import copy
   3:     nuevaCaja = copy.deepcopy(caja)
   4:     nuevaCaja.anchura = nuevaCaja.anchura + danchura
   5:     nuevaCaja.altura = nuevaCaja.altura + daltura
   6:     return nuevaCaja

Como ejercicio, rescriba mueveRect de modo que cree y devuelva un nuevo Rectangulo en lugar de modificar el viejo.

0