15.6 - Cópia

Alias podem tornar um programa difícil de ler porque as alterações em um lugar podem ter efeitos inesperados em outro lugar. É difícil monitorar todas as variáveis que podem referir-se a um dado objeto.

Em vez de usar alias, copiar o objeto pode ser uma alternativa. O módulo copy contém uma função chamada copy que pode duplicar qualquer objeto:

>>> p1 = Point()
>>> p1.x = 3.0
>>> p1.y = 4.0
>>> import copy
>>> p2 = copy.copy(p1)

p1 e p2 contêm os mesmos dados, mas não são o mesmo Point:

>>> print_point(p1)
(3, 4)
>>> print_point(p2)
(3, 4)
>>> p1 is p2
False
>>> p1 == p2
False

O operador is indica que p1 e p2 não são o mesmo objeto, que é o que esperamos. Porém, você poderia ter esperado que == fosse apresentado como True, porque esses pontos contêm os mesmos dados. Nesse caso, pode ficar desapontado ao saber que, para instâncias, o comportamento padrão do operador == é o mesmo que o do operador is; ele verifica a identidade dos objetos, não a sua equivalência. Isso acontece porque, para tipos definidos pelo programador, o Python não sabe o que deve ser considerado equivalente. Pelo menos, ainda não.

Se você usar copy.copy para duplicar um retângulo, descobrirá que ele copia o objeto Rectangle, mas não o Point embutido nele:

>>> box2 = copy.copy(box)
>>> box2 is box
False
>>> box2.corner is box.corner
True

A Figura 15.3 mostra como fica o diagrama de objeto. Esta operação chama-se cópia superficial porque copia o objeto e qualquer referência que contenha, mas não os objetos integrados.

Figura 15.3 – Diagrama: dois objetos Rectangle compartilhando o mesmo Point.
Figura 15.3 – Diagrama: dois objetos Rectangle compartilhando o mesmo Point.

Para a maior parte das aplicações, não é isso que você quer. Nesse exemplo, invocar grow_rectangle em um dos Rectangles não afetaria o outro, mas invocar move_rectangle em qualquer um deles afetaria a ambos! Esse comportamento é confuso e propenso a erros.

Felizmente, o módulo copy oferece um método chamado deepcopy que copia não só o objeto, mas também os objetos aos quais ele se refere, e os objetos aos quais estes se referem, e assim por diante. Você não se surpreenderá ao descobrir que esta operação se chama cópia profunda.

>>> box3 = copy.deepcopy(box)
>>> box3 is box
False
>>> box3.corner is box.corner
False
box3 e box são objetos completamente separados.

Como exercício, escreva uma versão de move_rectangle que cria e retorne um novo retângulo em vez de alterar o antigo.