Variáveis e valores

Ao criarmos uma variável em nosso código, na realidade estamos é criando um objeto e uma referência para esse objeto. Por exemplo, considere o código abaixo:

>>> x  = 100

O que fizemos foi “criar uma variável” com o valor 100 dentro dela, certo? Mais ou menos…  O que acontece, na realidade, é que 100 é um objeto do tipo inteiro que é alocado em algum lugar na memória. E x nada mais é do que uma referência (um ponteiro) para aquele objeto.

O código abaixo cria uma lista de 3 elementos e faz com que a variável x a referencie.

>>> x = [1, 2, 3]

Ao executar o código seguinte, o que irá acontecer? Errou quem acha que estamos fazendo uma cópia da lista [1, 2, 3] em y.

>>> y = x

Antes de explicar, vejamos seu conteúdo.

>>> print y
[1, 2, 3]

Ué, não é cópia mesmo? Vejamos:

>>> x.append(10)
>>> print y
[1, 2, 3, 10]

O que aconteceu? Adicionamos um valor ao final da “lista x” e ele apareceu milagrosamente na “lista y”? Não. Quando executamos y = x, fizemos com que a variável y passasse a apontar para o mesmo objeto apontado por x (a lista [1,2,3]). Assim, ao executarmos o append(10) sobre o objeto apontado por x, estamos modificando o objeto apontado por y também (que na realidade é o mesmo!).

Ou seja, ao executar y = x não fizemos uma cópia da lista, mas sim da referência a tal lista.

E como fazemos para copiar a lista? Bem, em Python existe um tipo de operação chamado de slicing, que funciona sobre objetos como as listas e que gera um novo objeto do mesmo tipo, com os elementos que estão contidos entre as posições especificadas pelos índices. Por exemplo:

>>> x = [1, 2, 3, 4, 5, 6, 7]
>>> print x[1:5]
[2, 3, 4, 5]

Essa operação gerou uma nova lista, contendo os valores das posições 1 até 5-1 (4). Se omitirmos os índices, estaremos nos referindo à lista como um todo, do início ao fim. Assim:

>>> print x[:]
[1, 2, 3, 4, 5, 6, 7]
>>> y = x[:]
>>> x.append(10)
>>> print x
[1, 2, 3, 4, 5, 6, 7, 10]
>>> print y
[1, 2, 3, 4, 5, 6, 7]

Como podemos ver no código acima, ao executarmos y = x[:], estamos gerando uma nova lista, com conteúdo idêntico à lista apontada por x, e fazendo com que y faça referência a ela. Então, para que façamos uma cópia de uma lista apontada pela variável x em uma variável y, fazemos o seguinte:

>>> y = x[:]

Então, lembre-se, para fazer uma cópia de uma lista inteira, podemos usar o “truque” do slicing e lembre-se também de que uma simples atribuição de uma variável para outra não copia a lista, mas sim a referência.

Porém, nem tudo são flores… Se a lista em questão contiver referências a outros objetos, ao invés dos objetos em si, a “cópia” feita será uma cópia rasa, pois os objetos referenciados dentro da lista não serão copiados. O contrário disso seria a cópia profunda (deep copy), onde os objetos referenciados na lista é que serão copiados, ao invés de apenas as suas referências.

Leia mais sobre as listas: http://docs.python.org/tutorial/datastructures.html