Trilha de aprendizado · Nível 3 · Tutorial 9

Controlar referências e cópias de coleções

Prever quando uma alteração será visível por outros nomes ou coleções e reconhecer os limites das cópias rasas em estruturas aninhadas.

  • Nível: Iniciante
  • Duração: 23 min
  • 8 passos
Controlar referências e cópias de coleções

O que você vai percorrer

  1. Dois nomes podem apontar para a mesma coleção Reconheça quando dois nomes dão acesso à mesma lista ou ao mesmo dicionário e preveja o que acontece ao alterar essa coleção. 2 min
  2. Reatribuir um nome não é modificar o objeto Distinga mudanças no objeto de mudanças na referência de um nome e preveja o que outro nome verá. 3 min
  3. Mesmo conteúdo não significa mesmo objeto Compare listas com == e is para distinguir conteúdo igual de referências ao mesmo objeto. 2 min
  4. Criar cópias rasas de listas e dicionários Use copy() para criar outro contêiner e alterar suas posições ou chaves sem mudar o contêiner original. 3 min
  5. Entender o que continua compartilhado na cópia Preveja quando uma alteração na cópia rasa alcança o original e quando fica apenas no contêiner copiado. 4 min
  6. Construir listas internas independentes Reconheça referências repetidas e construa uma nova lista interna a cada passagem do laço. 3 min
  7. Uma tupla não congela seus objetos internos Distinguir a substituição de uma posição da tupla da alteração de uma lista guardada nessa posição. 2 min
  8. Aplicar e revisar o controle de referências Analise um quadro de tarefas, corrija listas internas compartilhadas e justifique o que uma cópia rasa mantém em comum. 4 min

O que você vai aprender

  • Distinguir reatribuição de um nome de alteração do objeto ao qual ele se refere.
  • Identificar quando dois nomes apontam para a mesma coleção.
  • Diferenciar igualdade de conteúdo e identidade em exemplos com listas.
  • Criar cópias rasas e prever quais alterações internas continuam compartilhadas.
  • Evitar o compartilhamento acidental de listas internas e explicar por que uma tupla pode conter um objeto que muda.

Antes de começar

  • Consultar e atualizar coleções aninhadas
  • Agrupar e desempacotar valores com tuplas

Passo 1 de 8

Dois nomes podem apontar para a mesma coleção

Reconheça quando dois nomes dão acesso à mesma lista ou ao mesmo dicionário e preveja o que acontece ao alterar essa coleção.

Dois nomes, uma lista

O nome usado no código e a lista são coisas diferentes: o nome é uma forma de acessar o objeto. Dizemos que ele guarda uma referência à lista.

Ao executar outra = tarefas, você faz outra apontar para a mesma lista que tarefas. Essa atribuição não cria uma cópia.

python
tarefas = ["ler"]
outra = tarefas
Os nomes tarefas e outra têm setas que chegam à mesma lista, cujo conteúdo é ['ler']. Há dois nomes, mas apenas uma lista.

As setas representam referências. Alterar essa lista será visível pelos dois nomes.

Preveja a alteração

Leia o código e preveja as duas linhas exibidas.

python
tarefas = ["ler"]
outra = tarefas
outra.append("revisar")

print(tarefas)
print(outra)

Qual será a saída, na ordem dos prints?

Dicionários seguem a mesma regra

Atribuir um dicionário existente a outro nome também compartilha a referência. Neste exemplo, a atualização feita por painel pode ser consultada por pedido.

python
pedido = {"status": "aberto"}
painel = pedido
painel["status"] = "pronto"

print(pedido)
print(painel)

Exemplo

Saída

As duas linhas exibem {'status': 'pronto'}. Há um só dicionário, com dois nomes dando acesso a ele.

Reconheça o compartilhamento

Agora represente este trecho com um diagrama.

python
a = [7]
b = a

Qual diagrama representa o resultado dessas duas linhas?

Passo 2 de 8

Reatribuir um nome não é modificar o objeto

Distinga mudanças no objeto de mudanças na referência de um nome e preveja o que outro nome verá.

Mutação: o objeto muda

Quando dois nomes apontam para uma lista, alterar essa lista afeta o que ambos consultam. Essa alteração do próprio objeto se chama mutação. Tanto append quanto a atribuição a uma posição, como a[0] = 9, modificam a lista.

Os dois nomes continuam apontando para a mesma lista

python
a = [1, 2]
b = a

a.append(3)
a[0] = 9

print(a)  # [9, 2, 3]
print(b)  # [9, 2, 3]
Três estados: a e b apontam para uma única lista [1, 2]; depois de append, essa lista contém [1, 2, 3]; depois da atribuição ao índice zero, contém [9, 2, 3]. As referências permanecem iguais.

O conteúdo muda; as duas setas continuam chegando à mesma lista.

O alvo da atribuição

No exemplo anterior, a[0] = 9 faz apenas o nome a passar a apontar para outra lista, deixando b com [1, 2, 3].

Reatribuição: o nome muda de destino

Já a = [9] faz o nome a apontar para uma nova lista. Isso é reatribuição: a lista anterior não é modificada, e b continua apontando para ela. A partir daí, alterar a nova lista por a não altera a lista de b.

Um novo exemplo, desde o início

python
a = [1, 2]
b = a

a = [9]       # Reatribuição: muda o destino de a.
a.append(3)   # Mutação: altera a nova lista.

print(a)  # [9, 3]
print(b)  # [1, 2]
Antes, a e b apontam para [1, 2]. Após a = [9], a aponta para uma nova lista [9], enquanto b permanece com [1, 2]. Após a.append(3), só a nova lista passa a conter [9, 3].

Reatribuir muda uma seta. A mutação seguinte altera o objeto no destino dessa seta.

Preveja cada resultado

Ponto de partida

Para cada operação do exercício, comece novamente com estas duas linhas. As operações são independentes, não uma sequência.

python
a = [1, 2]
b = a

Associe cada operação ao tipo de mudança e ao estado final de a e b.

Toque em um item e depois no par correspondente.

Passo 3 de 8

Mesmo conteúdo não significa mesmo objeto

Compare listas com == e is para distinguir conteúdo igual de referências ao mesmo objeto.

Duas perguntas diferentes

Para listas, == pergunta: os conteúdos são iguais, na mesma ordem? Já is pergunta: é o mesmo objeto?

No exemplo, a e b apontam para uma única lista. A lista de c foi criada separadamente, embora tenha o mesmo conteúdo.

python
a = [10, 20]
b = a
c = [10, 20]

print(a == b)  # True
print(a is b)  # True
print(a == c)  # True
print(a is c)  # False
Os nomes a e b apontam para uma única lista [10, 20]. O nome c aponta para outra lista [10, 20], separada da primeira.

Conteúdos iguais podem estar em objetos distintos. As setas mostram quais nomes apontam para cada objeto.

Igualdade comprova identidade?

Se a == c resulta em True, então a is c também precisa resultar em True.

Escolha conforme a pergunta

Se você quer saber se duas listas têm os mesmos itens na mesma ordem, use ==. Não o substitua por is: isso mudaria a pergunta para identidade.

Exemplo

Comparar uma resposta

resposta = [2, 4]
esperado = [2, 4]

resposta == esperado resulta em True: o conteúdo corresponde ao esperado.

resposta is esperado resulta em False: são duas listas criadas separadamente. Isso não significa que a resposta esteja errada.

Dica

Use is quando a pergunta for sobre compartilhamento: “Estes dois nomes apontam para a mesma lista?”

Preveja após a alteração

Leia o código e preveja os resultados antes de executá-lo.

python
a = [10, 20]
b = a
c = [10, 20]

b.append(30)

print(a == b)
print(a is b)
print(a == c)

Quais são os três resultados, na ordem dos prints?

Passo 4 de 8

Criar cópias rasas de listas e dicionários

Use copy() para criar outro contêiner e alterar suas posições ou chaves sem mudar o contêiner original.

Outro nome ou outra lista?

outro = original compartilha a lista existente. Já copia = original.copy() cria uma nova lista: inicialmente, o conteúdo é igual, mas a identidade é diferente.

A cópia é rasa: copia as referências dos elementos para um novo contêiner externo, sem duplicar os objetos recursivamente. Aqui, vamos alterar apenas esse nível externo.

Alterações na nova lista

Inclusões, substituições e remoções na cópia não alteram as posições da lista original.

python
original = [10, 20]
outro = original
copia = original.copy()

print(outro is original)  # True
print(copia == original)  # True
print(copia is original)  # False

copia.append(30)
copia[0] = 99
copia.pop(1)

print(original)  # [10, 20]
print(copia)     # [99, 30]
original e outro apontam para a mesma lista, com 10 e 20. copia aponta para uma segunda lista, que também contém 10 e 20 antes das alterações.

Logo após copy(): duas listas distintas, apesar do conteúdo igual.

Crie a lista separada

Complete com o nome do método, sem parênteses, para que append não altere tarefas:

tarefas = ["ler", "revisar"]
rascunho = tarefas.____()
rascunho.append("enviar")

Dicionários também têm copy()

dicionario.copy() cria outro dicionário e preserva as referências aos valores. Assim como na lista, o conteúdo começa igual, mas o objeto externo é outro.

Você pode incluir uma chave, remover outra ou substituir um valor na cópia sem alterar essas associações no original.

Mude apenas o rascunho

Os valores deste exemplo são simples. Observe que todas as mudanças acontecem no dicionário rascunho.

python
estoque = {"lapis": 10, "canetas": 5}
rascunho = estoque.copy()

print(rascunho == estoque)  # True
print(rascunho is estoque)  # False

rascunho["lapis"] = 8
rascunho["borrachas"] = 3
del rascunho["canetas"]

print(estoque)
# {'lapis': 10, 'canetas': 5}
print(rascunho)
# {'lapis': 8, 'borrachas': 3}

Complete e preveja

Uma alteração no cadastro

Use este trecho para responder às duas questões. A lacuna representa o nome de um método.

python
cadastro = {"nome": "Ana", "pontos": 10}
edicao = cadastro.____()
edicao["pontos"] = 15
del edicao["nome"]

print(cadastro)

Qual método completa a lacuna para manter cadastro intacto? Digite sem parênteses.

Com a cópia criada corretamente, o que print(cadastro) mostra?

Passo 5 de 8

Entender o que continua compartilhado na cópia

Preveja quando uma alteração na cópia rasa alcança o original e quando fica apenas no contêiner copiado.

Duas listas externas, uma lista interna

A cópia rasa cria um contêiner externo novo, mas preserva as referências aos objetos internos. Assim, copia[0].append(...) alcança e modifica a mesma lista interna que original[0].

Identidades diferentes em um nível, iguais no outro

python
original = [["ler"]]
copia = original.copy()

print(original is copia)        # False
print(original[0] is copia[0])  # True

copia[0].append("praticar")
print(original)  # [['ler', 'praticar']]
print(copia)     # [['ler', 'praticar']]
Os nomes original e copia apontam para listas externas distintas. A posição zero de cada uma aponta para uma única lista interna com os textos ler e praticar.

O append altera a lista interna compartilhada, não cria uma segunda lista interna.

E se a posição receber outra lista?

Um novo exemplo, começando do zero

python
original = [["ler"]]
copia = original.copy()

copia[0] = ["praticar"]

print(original)
print(copia)

Quais são as duas saídas, na ordem?

O mesmo vale para valores de dicionários

No dicionário copiado, substituir o valor de uma chave afeta só a cópia. Já modificar uma lista que continua compartilhada afeta o que você vê pelos dois dicionários. Acompanhe as duas operações:

Substituir um valor e alterar outro por dentro

python
original = {"hoje": ["ler"], "amanha": ["revisar"]}
copia = original.copy()

copia["hoje"] = ["praticar"]
copia["amanha"].append("testar")

print(original)
# {'hoje': ['ler'], 'amanha': ['revisar', 'testar']}

print(copia)
# {'hoje': ['praticar'], 'amanha': ['revisar', 'testar']}

Dica

Localize o objeto alterado

copia["hoje"] = ... altera um vínculo no dicionário copiado. copia["amanha"].append(...) altera a lista alcançada por esse vínculo. Pergunte: quem mais aponta para o objeto que está sendo alterado?

Preveja as duas alterações em sequência

Leia e preveja as saídas antes de executar. Se quiser conferir, use seu próprio Python.

python
quadro = {"tarefas": ["ler"]}
rascunho = quadro.copy()

rascunho["tarefas"].append("revisar")
rascunho["tarefas"] = ["praticar"]

print(quadro["tarefas"])
print(rascunho["tarefas"])

Quais são as duas saídas? Explique por que a primeira alteração aparece em quadro, mas a segunda não.

Escreva pelo menos 30 caracteres (0/30).

Passo 6 de 8

Construir listas internas independentes

Reconheça referências repetidas e construa uma nova lista interna a cada passagem do laço.

Repetir não cria novas listas internas

Em [[0, 0]] * 3, o * repete as referências aos elementos da lista externa. Como ela contém uma única lista interna, as três posições passam a apontar para essa mesma lista. Alterar uma célula fica visível pelos três caminhos.

Três posições, uma lista interna

python
linhas = [[0, 0]] * 3
print(linhas[0] is linhas[1])  # True

linhas[0][0] = 9
print(linhas)  # [[9, 0], [9, 0], [9, 0]]
Uma lista externa tem três posições, de índices 0, 1 e 2. De cada posição sai uma seta para a mesma lista interna [9, 0].

O 9 não foi escrito em três listas: existe apenas uma lista interna, acessada por três posições.

Dica

Como obter independência

Crie a lista externa vazia antes do laço. Dentro de cada iteração, crie uma nova lista interna com [0, 0] e adicione-a à externa com append. Assim, cada posição receberá uma lista diferente.

Organize a construção

Ordene as etapas para construir três listas internas independentes.

  1. Ainda dentro do laço, executar linhas.append(linha).
  2. Criar linhas = [] antes do laço.
  3. Dentro do laço, criar linha = [0, 0].
  4. Iniciar o laço for numero in range(3):.

A criação precisa estar dentro do laço

Neste código, [0, 0] cria uma nova lista a cada passagem. O nome linha passa a apontar para ela, enquanto as listas anteriores continuam acessíveis pelas posições de linhas.

Construção independente

Execute este trecho no seu Python. Os comentários mostram os resultados esperados.

python
linhas = []
for numero in range(3):
    linha = [0, 0]
    linhas.append(linha)

print(linhas[0] is linhas[1])  # False
print(linhas[0] is linhas[2])  # False
print(linhas[1] is linhas[2])  # False

linhas[0][0] = 9
print(linhas)  # [[9, 0], [0, 0], [0, 0]]

Contraste: a lista criada fora é compartilhada

python
linha = [0, 0]
linhas = []
for numero in range(3):
    linhas.append(linha)

linhas[0][0] = 9
print(linhas)  # [[9, 0], [9, 0], [9, 0]]

Atenção

Usar for não basta

No segundo trecho, o append guarda três vezes a referência à mesma lista criada fora do laço. O que evita o compartilhamento é criar uma lista nova em cada iteração, não apenas trocar * por for.

Confira uma alteração localizada

No seu computador, use o trecho completo “Construção independente”. Troque apenas linhas[0][0] = 9 por linhas[1][1] = 7 e execute o código inteiro novamente. Relate os resultados das comparações com is, a lista final e por que a alteração ficou restrita a uma lista interna.

Escreva pelo menos 30 caracteres (0/30).

Passo 7 de 8

Uma tupla não congela seus objetos internos

Distinguir a substituição de uma posição da tupla da alteração de uma lista guardada nessa posição.

A referência fica; a lista pode mudar

A imutabilidade da tupla vale para suas posições: você não pode substituir as referências que elas guardam. Mas, se uma posição aponta para uma lista, essa lista continua mutável.

No exemplo, registro[1] dá acesso à lista. O append modifica essa lista, não a posição da tupla.

Alteração permitida

python
registro = ("Ana", ["Python"])
registro[1].append("SQL")

print(registro)
# ('Ana', ['Python', 'SQL'])
Antes e depois do append: a tupla registro mantém duas posições. A posição 0 aponta para Ana e a posição 1 aponta para a mesma lista, que passa de ['Python'] para ['Python', 'SQL'].

A seta da posição 1 não muda de destino. Só o conteúdo da lista muda.

O que o append alterou?

No exemplo anterior, registro[1].append("SQL") substituiu a lista por outra na posição 1 da tupla.

Substituir a posição é diferente

Já uma atribuição a registro[1] tenta colocar outra referência nessa posição. A tupla não permite essa substituição, mesmo que o novo valor também seja uma lista.

Substituição não permitida

Se executar este trecho no seu Python, ele será interrompido na segunda linha com um TypeError.

python
registro = ("Ana", ["Python"])
registro[1] = ["Python", "SQL"]
# TypeError: a tupla não permite atribuição a uma posição.

Dica

Localize o alvo da alteração

Em registro[1] = ..., o alvo é uma posição da tupla. Em registro[1].append(...), o alvo é a lista alcançada por essa posição. O que importa é qual objeto a operação tenta modificar.

Explique a diferença

Considere pacote = ("cursos", ["Python", "SQL"]).

A: pacote[1].pop()
B: pacote[1] = []

Avalie cada operação separadamente, partindo da tupla original. Qual é permitida? Como fica o conteúdo de pacote depois dela? Explique por que isso não contradiz a imutabilidade da tupla.

Escreva pelo menos 30 caracteres (0/30).

Passo 8 de 8

Aplicar e revisar o controle de referências

Analise um quadro de tarefas, corrija listas internas compartilhadas e justifique o que uma cópia rasa mantém em comum.

Escolha pelo objeto que vai mudar

Um quadro, três decisões

Para trabalhar com um quadro de tarefas, escolha conforme a intenção:

  • Outro nome para o mesmo quadro: atalho = quadro.
  • Outro dicionário para substituir, incluir ou remover entradas sem alterar as entradas do original: rascunho = quadro.copy().
  • Novas listas internas independentes: crie uma lista nova a cada iteração, em vez de repetir a referência à mesma lista.

Separados por fora, compartilhados por dentro

Os nomes quadro e atalho apontam para um único dicionário. O nome rascunho aponta para outro dicionário. A entrada hoje dos dois dicionários aponta para a mesma lista contendo ler.

Logo após a cópia: atalho is quadro é True; rascunho is quadro é False; rascunho['hoje'] is quadro['hoje'] é True.

Dica

Siga as setas

Reatribuir um nome muda sua seta. Substituir uma entrada muda a seta daquela entrada. Modificar uma lista muda o objeto alcançado pelas setas. Conteúdo igual (==) não prova identidade (is).

Preveja antes de executar

O rascunho e as colunas

Leia o script sem executá-lo ainda. A intenção é ter um rascunho do quadro e duas colunas independentes.

python
quadro = {"hoje": ["ler"], "amanha": []}
atalho = quadro
rascunho = quadro.copy()

rascunho["hoje"].append("revisar")
rascunho["amanha"] = ["praticar"]

colunas = [[]] * 2
colunas[0].append("ler")

print(quadro)
print(rascunho)
print(atalho is quadro, rascunho is quadro)
print(colunas)

Onde a alteração aparece?

Preveja as quatro linhas impressas. Explique por que “revisar” aparece no quadro original, “praticar” não aparece nele e “ler” aparece nas duas colunas.

Escreva pelo menos 30 caracteres (0/30).

Corrija e confira no seu computador

Independência onde ela é necessária

No seu editor, substitua a construção de colunas por um laço que crie uma lista nova a cada passagem. Compare sua solução com a versão completa abaixo e execute-a com Python. O compartilhamento de hoje foi mantido de propósito: a correção é apenas das colunas.

Versão ajustada

python
quadro = {"hoje": ["ler"], "amanha": []}
atalho = quadro
rascunho = quadro.copy()

rascunho["hoje"].append("revisar")
rascunho["amanha"] = ["praticar"]

colunas = []
for numero in range(2):
    coluna = []
    colunas.append(coluna)
colunas[0].append("ler")

print(quadro)
print(rascunho)
print(atalho is quadro, rascunho is quadro)
print(colunas)
print(colunas[0] is colunas[1])
print(quadro["hoje"] is rascunho["hoje"])

Justifique a solução

Relate o resultado de print(colunas) e das duas últimas comparações. Por que a correção isolou as colunas, mas não a lista hoje? Para substituir uma entrada sem substituir a do quadro original, você usaria atalho ou rascunho?

Escreva pelo menos 40 caracteres (0/40).

Checklist para a próxima alteração

Resumo

Antes de mudar uma coleção

Use estas perguntas para prever o efeito, em vez de confiar apenas na aparência dos valores.

  • Qual objeto será alterado? Diferencie reatribuir um nome, substituir uma entrada e modificar um objeto interno.
  • Quem aponta para esse objeto? Outros nomes ou posições podem revelar a mesma alteração.
  • Quais referências a cópia preserva? Uma cópia rasa cria outro contêiner externo, mas não duplica os objetos internos.
  • Preciso de listas internas independentes? Crie uma nova a cada iteração; repetir ou reutilizar a mesma lista compartilha referências.
  • Como conferir? Use == para conteúdo, is para identidade e observe o efeito de uma alteração localizada.
  • Há uma tupla envolvida? Suas posições são fixas, mas uma lista guardada nela ainda pode mudar.

Tutorial concluído

Parabéns! Você concluiu: Controlar referências e cópias de coleções

Você praticou como escolher entre compartilhar uma referência, criar uma cópia rasa e construir listas internas independentes. A regra central é: identifique o objeto que muda e siga as referências até ele.

Baixe o Aplicativo agora para ter acesso a + de 5000 cursos gratuitos, exercícios, certificado e muito conteúdo sem pagar nada!

  • Cursos online 100% gratuitos do início ao fim

    Milhares de cursos online em vídeo, ebooks e áudiobooks.

  • Mais de 60 mil exercícios gratuitos

    Para testar seus conhecimentos no decorrer dos cursos online

  • Certificado Digital gratuito válido em todo o Brasil

    Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail

Aplicativo Cursa na tela de ebook, na tela de curso em vídeo e na tela de exercícios do curso, mais o certificado de conclusão de curso