Blog

Cópia rasa vs cópia profunda em Python

Atribuir uma lista a um segundo nome não copia nada. Os dois nomes apontam para o mesmo objeto, então uma mudança feita por um é visível pelo outro.

Copiar tem dois níveis: uma cópia rasa cria um objeto externo novo contendo os mesmos objetos internos, e uma cópia profunda reconstrói a árvore inteira.

Atribuição não é cópia

Uma lista, dois nomes.

# assignment does not copy
original = [1, 2, 3]
alias = original
alias.append(4)
print(original, alias is original)

Ele imprime:

[1, 2, 3, 4] True

Uma cópia rasa

.copy(), list(x) e x[:] fazem a mesma coisa para uma lista.

# a shallow copy is a new outer object
shallow = original.copy()          # list(original) and original[:] do the same
shallow.append(5)
print(original, shallow, shallow is original)

Ele imprime:

[1, 2, 3, 4] [1, 2, 3, 4, 5] False

A lista de fora agora é separada.

Onde a cópia rasa para

A lista nova guarda as mesmas listas internas da antiga.

# shallow means one level
rows = [[1, 2], [3, 4]]
copied = rows.copy()
copied[0].append(99)
print(rows, copied)
print('inner list shared:', rows[0] is copied[0])

Ele imprime:

[[1, 2, 99], [3, 4]] [[1, 2, 99], [3, 4]]
inner list shared: True

As duas listas de fora mostram a mudança, porque existe uma lista interna só.

Cópia profunda

copy.deepcopy percorre a estrutura e copia cada objeto que encontra.

# deepcopy copies the whole tree
import copy

rows2 = [[1, 2], [3, 4]]
deep = copy.deepcopy(rows2)
deep[0].append(99)
print(rows2, deep)
print('inner list shared:', rows2[0] is deep[0])

Ele imprime:

[[1, 2], [3, 4]] [[1, 2, 99], [3, 4]]
inner list shared: False

A mudança fica contida.

Dicionários e configuração aninhada

Esse é o caso comum do mundo real: um dicionário de configuração que algum outro código altera.

# it works on dicts and objects too
config = {'db': {'host': 'localhost', 'ports': [5432]}}
shallow_cfg = copy.copy(config)
deep_cfg = copy.deepcopy(config)
config['db']['ports'].append(5433)
print('shallow sees the change:', shallow_cfg['db']['ports'])
print('deep does not:', deep_cfg['db']['ports'])

Ele imprime:

shallow sees the change: [5432, 5433]
deep does not: [5432]

Ciclos são tratados

O deepcopy mantém um registro do que já copiou, então uma estrutura que aponta para si mesma não causa recursão infinita.

# cycles are handled
node = {'name': 'root'}
node['self'] = node
clone = copy.deepcopy(node)
print(clone['self'] is clone, clone is node)

Ele imprime:

True False

A cópia aponta para a cópia.

Uma tupla não protege o que está dentro dela

Uma tupla não pode ser reatribuída, mas as listas dentro dela ainda podem ser alteradas — e uma cópia rasa as compartilha.

# tuples of mutables are still shared by a shallow copy
data = ([1, 2], [3, 4])
shallow_t = copy.copy(data)
shallow_t[0].append(3)
print(data, shallow_t[0] is data[0])

Ele imprime:

([1, 2, 3], [3, 4]) True

Contêineres imutáveis guardam conteúdo mutável. Vale lembrar disso quando você usa uma tupla como valor padrão.

O que lembrar

  • b = a cria um segundo nome, não um segundo objeto.

  • Cópias rasas duplicam só o contêiner de fora.

  • copy.deepcopy duplica a árvore inteira e lida com ciclos.

  • Cópia profunda é lenta. Use quando precisar, não por padrão.

Se você está fazendo cópia profunda com frequência, normalmente é sinal de que os dados deveriam ser imutáveis. Uma dataclass congelada ou uma tupla de tuplas elimina a pergunta.

How useful was this post?

Click on a heart to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.