Entendendo Cópias Superficiais e Profundas em Python

O operador de atribuição em Python cria referências aos objetos

Python distingue entre tipos de dados mutáveis e imutáveis:

  • Tipos mutáveis: listas e dicionários
  • Tipos imutáveis: strings, números inteiros, números decimais e tuplas

Comportamento dos tipos imutáveis

Para dados imutáveis, o endereço de memória permanece constante durante toda a existência do objeto.

# Exemplo com número inteiro:
endereco_a = id(42)
print(f"Endereço de memória: {endereco_a}")
# Resultado: 140716627437504

endereco_b = id(42)
print(f"Endereço de memória: {endereco_b}")
# Resultado: 140716627437504

Para tipos imutáveis, não há diferença entre atribuição simples, cópia rasa ou cópia profunda, pois o conteúdo nunca é modificado in-place.

x = 100
y = x
print(f"x={x}, Endereço x: {id(x)}")
print(f"y={y}, Endereço y: {id(y)}")
# Resultado:
# x=100, Endereço x: 140716627441152
# y=100, Endereço y: 140716627441152

Comportamento dos tipos mutáveis

Objetos mutáveis criam novos endereços de memória a cada criação, mesmo com valores идентичных.

# Duas listas com mesmo conteúdo possuem endereços diferentes:
endereco_primeiro = id([10, 20])
print(f"Primeiro: {endereco_primeiro}")
# Resultado: 2234567890123

endereco_segundo = id([10, 20])
print(f"Segundo: {endereco_segundo}")
# Resultado: 2234567890456

Para referências independentes de objetos mutáveis, utiliza-se cópia rasa ou profunda.

# Atribuição simples - compartilhamento de referência:
dados = [1, 2, 3, ['x', 'y']]
referencia = dados

print(f"dados: {dados}")
print(f"dados Endereço: {id(dados)}")
print(f"referencia: {referencia}")
print(f"referencia Endereço: {id(referencia)}")
print("=" * 20)

dados.extend(referencia)
print(f"dados: {dados}")
print(f"dados Endereço: {id(dados)}")
print(f"referencia: {referencia}")
print(f"referencia Endereço: {id(referencia)}")

# Resultado:
# dados: [1, 2, 3, ['x', 'y']]
# dados Endereço: 2234567890100
# referencia: [1, 2, 3, ['x', 'y']]
# referencia Endereço: 2234567890100
# ====================
# dados: [1, 2, 3, ['x', 'y'], 1, 2, 3, ['x', 'y']]
# dados Endereço: 2234567890100
# referencia: [1, 2, 3, ['x', 'y'], 1, 2, 3, ['x', 'y']]
# referencia Endereço: 2234567890100

Cópia Superficial (Shallow Copy)

O método copy() cria um novo objeto, porém mantém referências aos objetos aninhados originais.

# Cenário 1: Operações no objeto principal não afetam a cópia
numeros = [5, 10, 15, ['a', 'b']]
copia = numeros.copy()

numeros.extend(copia)
print(f"numeros: {numeros}")
print(f"numeros Endereço: {id(numeros)}")
print(f"copia: {copia}")
print(f"copia Endereço: {id(copia)}")

# Resultado:
# numeros: [5, 10, 15, ['a', 'b'], 5, 10, 15, ['a', 'b']]
# numeros Endereço: 2234567890200
# copia: [5, 10, 15, ['a', 'b']]
# copia Endereço: 2234567890300

# Cenário 2: Modificações em objetos aninhados afetam ambos
valores = [5, 10, 15, ['a', 'b']]
copia = valores.copy()

print(f"valores: {valores}")
print(f"valores Endereço: {id(valores)}")
print(f"copia: {copia}")
print(f"copia Endereço: {id(copia)}")
print("=" * 20)

valores.extend(copia)
valores[3].extend(['m', 'n'])

print(f"valores: {valores}")
print(f"valores Endereço: {id(valores)}")
print(f"copia: {copia}")
print(f"copia Endereço: {id(copia)}")

# Resultado:
# valores: [5, 10, 15, ['a', 'b', 'm', 'n'], 5, 10, 15, ['a', 'b', 'm', 'n']]
# valores Endereço: 2234567890200
# copia: [5, 10, 15, ['a', 'b', 'm', 'n']]
# copia Endereço: 2234567890300

Cópia Profunad (Deep Copy)

O método deepcopy() do módulo copy cria uma cópia completamente independente, inculindo todos os objetos aninhados.

import copy

elementos = [5, 10, 15, ['a', 'b']]
copia_completa = copy.deepcopy(elementos)

elementos.extend(copia_completa)
elementos[3].extend(['m', 'n'])

print(f"elementos: {elementos}")
print(f"elementos Endereço: {id(elementos)}")
print(f"copia_completa: {copia_completa}")
print(f"copia_completa Endereço: {id(copia_completa)}")

# Resultado:
# elementos: [5, 10, 15, ['a', 'b', 'm', 'n'], 5, 10, 15, ['a', 'b']]
# elementos Endereço: 2234567890400
# copia_completa: [5, 10, 15, ['a', 'b']]
# copia_completa Endereço: 2234567890500

Quando utilizar cada tipo

A cópia superficial é suficiente quando:

  • A estrutura de dados contém apenas tipos imutáveis
  • Você precisa de desempenho otimizado e a estrutura é simples

A cópia profunda é recomendada quando:

  • A estrutura possui objetos aninhados mutáveis
  • É necesssário isolamento completo entre os dados originais e a cópia
  • A memória disponível permite o uso de cópias independentes

Recomenda-se utilizar cópia profunda sempre que possível, pois garante independência total dos dados e evita comportamentos inesperados em alterações subsequentes.

Publicado em 6-8 05:24 por Thomas