Copy e deepcopy em Python: por que sua "cópia" não é uma cópia
Este tutorial foi traduzido do original em inglês.Read in EnglishLeer en españolЧитать на русскомอ่านภาษาไทยAvisar sobre um erro de tradução
Imagine que você tem uma pasta cheia de fotos. Você faz uma cópia da pasta e entrega para um amigo. Mas tem um detalhe: só a pasta é nova. As fotos lá dentro são as mesmas fotos físicas, compartilhadas pelas duas pastas. Se o seu amigo desenhar um bigode em uma delas, a sua foto também fica com o bigode.
É exatamente assim que a cópia padrão do Python funciona. Você acha que fez uma cópia independente, mas os objetos internos continuam compartilhados. É um dos bugs mais confusos do Python, e ele pega tanto iniciantes quanto desenvolvedores experientes.
Neste tutorial, você vai aprender a diferença entre atribuição, cópia rasa e cópia profunda. Ao final, você vai saber exatamente quando usar cada uma e nunca mais vai cair em um bug de "referência compartilhada".
Por que mudar uma "cópia" muda o original?
Vamos começar pela surpresa mais comum. Quando você escreve b = a, você NÃO está fazendo uma cópia. Está criando um segundo nome para exatamente o mesmo objeto. Tanto a quanto b apontam para os mesmos dados na memória.
[1, 2, 3, 4] [1, 2, 3, 4]
Isso acontece porque a e b são duas etiquetas coladas na mesma caixa. Quando você coloca algo novo na caixa usando a etiqueta b, também vê essa mudança ao olhar pela etiqueta a.
Você pode confirmar que duas variáveis apontam para o mesmo objeto usando o operador is ou comparando o id() delas.
True 80036304 80036304
O que é uma cópia rasa?
Uma cópia rasa (shallow copy) cria um novo contêiner externo, mas NÃO copia os objetos que estão dentro dele. A nova lista é independente no nível de cima, mas os objetos aninhados continuam compartilhados com o original.
Existem várias formas de fazer uma cópia rasa de uma lista.
[1, 2, 3] [1, 2, 3, 99]
Para dicionários, use .copy() ou dict().
{'name': 'Alice', 'age': 25}
{'name': 'Alice', 'age': 30}Parece perfeito. Mas aqui está a armadilha: a cópia rasa só copia um nível. Se a sua lista contém outras listas (ou dicionários), esses objetos internos continuam compartilhados.
3 4 [1, 2, 99] [1, 2, 99]
original = [[1, 2], [3, 4]]
shallow = original.copy()
shallow[0].append(99)
print(original) # [[1, 2, 99], [3, 4]]import copy
original = [[1, 2], [3, 4]]
deep = copy.deepcopy(original)
deep[0].append(99)
print(original) # [[1, 2], [3, 4]]O que é uma cópia profunda e quando você precisa dela?
Uma cópia profunda (deep copy) cria um clone totalmente independente. Ela copia o objeto externo E copia, de forma recursiva, cada objeto mutável dentro dele, não importa quantos níveis de aninhamento existam. A cópia não compartilha com o original nada que possa ser alterado: só os valores que não mudam, como números e strings, são reaproveitados.
Para fazer uma cópia profunda, você precisa do módulo copy da biblioteca padrão do Python.
[[1, 2], [3, 4], [5, 6]] [[1, 2, 99], [3, 4], [5, 6], [7, 8]]
A cópia profunda também funciona com dicionários, inclusive em estruturas com muitos níveis de aninhamento.
Alice [90, 85, 92] ['star', 'gold'] Bob [90, 85, 92, 100] ['star', 'gold', 'diamond']
Por dentro: como as referências funcionam em Python
Para entender de verdade como as cópias funcionam, você precisa entender como o Python guarda os dados. Cada dado vive em algum lugar da memória como um objeto. Variáveis são apenas nomes que referenciam (apontam para) esses objetos.
A atribuição copia a referência. A cópia rasa cria um novo contêiner com as mesmas referências dentro. A cópia profunda cria um novo contêiner cujas referências apontam para novas cópias de cada objeto mutável que está dentro.
True True False
Aqui está uma tabela que resume as três abordagens.
Assignment: same outer, same inner Shallow copy: new outer, same inner Deep copy: new outer, new inner
Quando usar cada tipo de cópia?
Use a atribuição (b = a) quando você quer, de propósito, dois nomes para os mesmos dados. Isso é comum ao passar dados para funções: normalmente você quer que a função trabalhe com o original.
Use a cópia rasa quando você tem uma lista ou um dicionário plano (sem objetos mutáveis aninhados) e precisa de uma cópia independente. Isso cobre a maioria dos casos do dia a dia.
Use a cópia profunda quando você tem objetos mutáveis aninhados (listas de listas, dicionários com listas como valores etc.) e precisa de um clone totalmente independente. Isso é essencial quando você quer modificar a cópia sem nenhum risco de afetar o original.
Exercícios práticos
Leia o código com atenção. O que ele vai exibir? Lembre-se de que = com listas cria um apelido, não uma cópia.
Digite a saída exata.
O código abaixo tenta criar um backup da lista original antes de modificá-la. Mas o backup também muda! Corrija o código para que o backup fique inalterado.
Saída esperada:
Original: [1, 2, 3, 4]
Backup: [1, 2, 3]Este código usa uma cópia rasa em dados aninhados. Leia com atenção e preveja o que será exibido.
Digite a saída exata.
Crie uma cópia totalmente independente dos dados de original usando cópia profunda. Depois, adicione a nota 100 à lista de notas da cópia. Exiba as notas do original e as notas da cópia em linhas separadas, para provar que elas são independentes.
Este jogo salva um checkpoint do estado do jogador e, em seguida, o jogador pega um item. Mas, quando carregamos o checkpoint, o item já está lá! Corrija o código para que o checkpoint seja um retrato realmente independente.
Saída esperada:
Checkpoint inventory: ['sword', 'shield']
Current inventory: ['sword', 'shield', 'potion']