Python Copiando Listas
A irmã da Ana vai ao mercado também e pede: "me passa uma cópia da sua lista, que eu tiro o que eu não quero e ponho as minhas coisas". A Ana escreve lista_irma = lista_ana, a irmã tira o café e põe chocolate… e o café some da lista da Ana também. Nesta aula você vai entender por que o = não copia uma lista, e os jeitos certos de fazer uma cópia de verdade.
- O que
b = afaz de verdade com listas: dois nomes para a mesma lista. - A diferença entre
is("é a mesma lista?") e==("têm os mesmos itens?"). - Os três jeitos de copiar:
a.copy(),list(a)ea[:]. - O limite da cópia: listas dentro de listas continuam compartilhadas (a cópia rasa).
- Os erros reais: o
copysem parênteses e a grade[[0, 0]] * 3.
A ideia: duas etiquetas na mesma caixa
Até aqui, você pensou numa variável como uma caixa com etiqueta (aula Variáveis). Para listas, a imagem mais exata é outra: a lista é uma caixa, e a variável é uma etiqueta colada nela. Quando você escreve:
lista_ana = ["arroz", "feijão", "café"] lista_irma = lista_ana
a primeira linha cria a caixa (a lista) e cola nela a etiqueta lista_ana. A segunda linha não cria caixa nenhuma: ela pega uma segunda etiqueta, lista_irma, e cola na mesma caixa. Existe uma lista só, com dois nomes. Tudo o que você fizer usando um nome, o outro vê, porque é a mesma lista.
É como a lista de compras compartilhada no celular: a Ana e a irmã abrem a mesma lista, e o que uma risca, some para as duas. Às vezes é isso que você quer. Quando não é, você precisa de uma cópia: uma caixa nova, com os mesmos itens.
1. b = a não copia
lista_ana = ["arroz", "feijão", "café"]
lista_irma = lista_ana
lista_irma.remove("café")
lista_irma.append("chocolate")
print("Ana: ", lista_ana)
print("Irmã:", lista_irma)
$ python3 igual.py Ana: ['arroz', 'feijão', 'chocolate'] Irmã: ['arroz', 'feijão', 'chocolate']
A irmã mexeu só na lista_irma, mas a lista da Ana perdeu o café e ganhou o chocolate. Nenhum erro, nenhum aviso: o Python fez exatamente o que foi pedido. O remove e o append mudaram a lista, e as duas etiquetas estão nela.
Como ver isso: is e ==
O operador is ("é", aula is e in) pergunta se dois nomes estão na mesma lista. O == pergunta se duas listas têm os mesmos itens, mesmo que sejam caixas diferentes:
>>> a = ["arroz", "feijão"]
>>> b = a
>>> b is a
True
>>> c = a.copy()
>>> c is a
False
>>> c == a
True
>>> b.append("sal")
>>> a
['arroz', 'feijão', 'sal']
>>> c
['arroz', 'feijão']
b is aéTrue:beasão a mesma lista.c is aéFalse: ocopy()criou uma lista nova. Masc == aéTrue: por enquanto, as duas têm os mesmos itens.- O
appendfeito porbapareceu ema(são a mesma), mas não emc(é outra).
b?
Mudar a lista (com append, remove, b[0] = …) é diferente de dar outra lista para a variável. Um = com uma lista nova descola a etiqueta e a cola numa caixa nova; a lista antiga não muda:
>>> a = ["arroz", "feijão"] >>> b = a >>> b = ["pão"] >>> a ['arroz', 'feijão'] >>> b ['pão']
É por isso que, com números e textos, b = a nunca causa essa surpresa: eles são imutáveis, então não há como "mudar a caixa"; toda mudança é um = com um valor novo. A aula Mutável × Imutável conta essa história inteira.
Missão: conserte a segunda linha para a irmã receber uma cópia da lista. A lista da Ana tem de continuar com o café.
lista_ana = ["arroz", "feijão", "café"]
lista_irma = lista_ana
lista_irma.remove("café")
lista_irma.append("chocolate")
print("Ana: ", lista_ana)
print("Irmã:", lista_irma)
lista_ana = ["arroz", "feijão", "café"]
lista_irma = lista_ana.copy()
lista_irma.remove("café")
lista_irma.append("chocolate")
print("Ana: ", lista_ana)
print("Irmã:", lista_irma)
2. Copiando de verdade: copy()
lista_ana = ["arroz", "feijão", "café"]
lista_irma = lista_ana.copy()
lista_irma.remove("café")
lista_irma.append("chocolate")
print("Ana: ", lista_ana)
print("Irmã:", lista_irma)
$ python3 copia.py Ana: ['arroz', 'feijão', 'café'] Irmã: ['arroz', 'feijão', 'chocolate']
- lista_ana.copy()
- O método
copy("copiar") cria uma lista nova, com os mesmos itens, na mesma ordem, e a devolve. Os parênteses são obrigatórios: é uma chamada. - lista_irma = …
- A lista nova ganha a etiqueta
lista_irma. Agora são duas caixas, cada uma com o seu nome.
Agora cada uma mexe na sua. A Ana continua com o café; a irmã tem o chocolate.
3. Três jeitos de copiar
Além do copy(), há outros dois jeitos muito comuns, e você vai encontrar os três em programas dos outros:
original = ["arroz", "feijão", "café"]
copia1 = original.copy()
copia2 = list(original)
copia3 = original[:]
original.append("leite")
print("original:", original)
print("copia1: ", copia1)
print("copia2: ", copia2)
print("copia3: ", copia3)
$ python3 tres_jeitos.py original: ['arroz', 'feijão', 'café', 'leite'] copia1: ['arroz', 'feijão', 'café'] copia2: ['arroz', 'feijão', 'café'] copia3: ['arroz', 'feijão', 'café']
O leite entrou só na original: as três cópias são listas independentes.
| Jeito | Como funciona | Quando usar |
|---|---|---|
b = a.copy() | O método de cópia da lista | O mais claro: quem lê sabe na hora que é uma cópia. Prefira este |
b = list(a) | A função list() (aula Listas) cria uma lista nova com os itens de a | Quando a pode não ser uma lista (uma tupla, um range) e você quer uma lista |
b = a[:] | Uma fatia do começo ao fim (aula Acessando Itens) é sempre uma lista nova | Muito comum em código antigo; curto, mas menos óbvio para quem está começando |
b = a | Não copia: outro nome para a mesma lista | Só quando você quer mesmo que os dois nomes vejam as mesmas mudanças |
Você já usou um desses sem perceber: na aula Removendo Itens, o for nota in notas[:] percorria uma cópia enquanto o remove mexia na original.
4. O limite: listas dentro de listas
Os três jeitos fazem uma cópia rasa (em inglês, shallow copy): a caixa de fora é nova, mas o que está dentro dela não é copiado. Se um item for uma lista, as duas caixas guardam a mesma lista de dentro. Veja com a turma, em que cada aluno é um par [nome, nota]:
turma = [["Ana", 9.5], ["Bruno", 7.0]]
copia = turma.copy()
copia.append(["Carla", 8.0])
copia[0][1] = 10.0
print("turma:", turma)
print("copia:", copia)
$ python3 rasa.py turma: [['Ana', 10.0], ['Bruno', 7.0]] copia: [['Ana', 10.0], ['Bruno', 7.0], ['Carla', 8.0]]
- A Carla entrou só na
copia: oappendmexeu na caixa de fora, que é nova. Até aqui, a cópia funcionou. - Mas a nota da Ana virou 10.0 nas duas.
copia[0]é o par da Ana, e esse par é o mesmo nas duas listas: a cópia rasa copiou a etiqueta do par, e não o par.
Para listas de textos e números, como a lista de compras, a cópia rasa é tudo de que você precisa. Para listas de listas, existe a cópia profunda, que copia também o que está dentro. Ela tem aula própria: Cópia Rasa e Cópia Profunda.
Quando dá errado
O sintoma do igual.py: nenhum erro, mas uma lista que você não tocou mudou sozinha. A causa é sempre a mesma: em algum lugar, um b = a (ou um a passado para algum lugar e guardado com outro nome) fez os dois nomes apontarem para a mesma lista. Como descobrir: print(b is a); se der True, é a mesma lista. Como consertar: b = a.copy() no lugar onde a "cópia" foi feita.
copy sem parênteses
lista_ana = ["arroz", "feijão", "café"]
lista_irma = lista_ana.copy
print(lista_irma)
lista_irma.append("chocolate")
$ python3 sem_parenteses.py
<built-in method copy of list object at 0x7fa8813b5d40>
Traceback (most recent call last):
File "/home/ana/sem_parenteses.py", line 4, in <module>
lista_irma.append("chocolate")
^^^^^^^^^^^^^^^^^
AttributeError: 'builtin_function_or_method' object has no attribute 'append'
Linha por linha:
- Linha 2: sem os parênteses, o
copynão foi chamado. A variávellista_irmarecebeu o próprio método, a "ferramenta de copiar", e não uma lista. - Linha 3: o
printmostra isso:<built-in method copy of list object at 0x…>, "método embutidocopyde um objeto lista". O número depois doaté um endereço da memória e muda a cada execução. - Linha 4:
AttributeError: 'builtin_function_or_method' object has no attribute 'append': um método não temappend, porque não é uma lista.
Como consertar: lista_ana.copy(), com os parênteses.
[[0, 0]] * 3: três vezes a mesma linha
A professora quer uma grade de notas com 3 alunos e 2 provas, começando tudo em zero. Ela lembrou do * da aula Adicionando Itens:
grade = [[0, 0]] * 3 print(grade) grade[0][0] = 5 print(grade)
$ python3 grade.py [[0, 0], [0, 0], [0, 0]] [[5, 0], [5, 0], [5, 0]]
Ela mudou a primeira prova do primeiro aluno, e mudou a dos três. O * 3 não copiou a lista [0, 0] três vezes: ele pôs a mesma lista três vezes na grade, como três etiquetas na mesma caixa. (Com [0] * 3 não há problema, porque o 0 é um número, imutável.)
Como consertar: criar uma lista nova para cada linha. O jeito curto é uma list comprehension (aula List Comprehension), que roda [0, 0] de novo a cada volta:
>>> grade = [[0, 0] for i in range(3)] >>> grade[0][0] = 5 >>> grade [[5, 0], [0, 0], [0, 0]]
Você copiou com copy(), mudou um item de dentro de um item (copia[0][1] = …) e a original mudou também: é o limite da cópia rasa, da seção 4. Como consertar: veja a cópia profunda, em Cópia Rasa e Cópia Profunda.
Referência rápida
| Escrita | O que faz | Lista nova? |
|---|---|---|
b = a | Mais um nome para a mesma lista | Não |
b = a.copy() | Cópia rasa (o jeito mais claro) | Sim |
b = list(a) | Cópia rasa; aceita qualquer sequência | Sim |
b = a[:] | Cópia rasa por fatia | Sim |
b is a | True se são a mesma lista | — |
b == a | True se têm os mesmos itens, na mesma ordem | — |
[[0, 0] for i in range(3)] | Uma lista de listas, cada uma nova | Sim |
| Sintoma | Causa provável |
|---|---|
| Mexi numa lista e outra mudou junto | b = a em vez de b = a.copy() |
<built-in method copy of list object at 0x…> | a.copy sem parênteses |
AttributeError: 'builtin_function_or_method' object has no attribute 'append' | A variável guarda o método, não a lista (faltaram os parênteses) |
| Mudei uma linha da grade e todas mudaram | [[…]] * n: use [[…] for i in range(n)] |
| Mudei um item de dentro na cópia e a original mudou | Cópia rasa: veja Cópia Rasa e Cópia Profunda |
Exercícios
1. Uma cópia de verdade
Complete com o método que copia a lista:
2. Cópia por fatia
Complete a fatia que copia a lista inteira:
3. A mesma lista?
Depois de a = [1, 2] e b = a, o que b is a responde?
4. Depois de a = [1, 2], b = a e b.append(3), quanto vale a?
5. Qual destas linhas não cria uma lista nova?
6. Com c = a.copy(), o que c == a e c is a respondem logo depois da cópia?
Missão final: a lista do mês
Missão: a Ana quer montar a lista do mês a partir da lista da semana, acrescentando sabão e detergente, mas a lista da semana está sendo estragada. Faça mes ser uma cópia de semana.
semana = ["arroz", "feijão", "café"]
mes = semana
mes.extend(["sabão", "detergente"])
print("Semana:", semana)
print("Mês:", mes)
semana = ["arroz", "feijão", "café"]
mes = semana.copy()
mes.extend(["sabão", "detergente"])
print("Semana:", semana)
print("Mês:", mes)
Outro jeito, sem cópia nenhuma: mes = semana + ["sabão", "detergente"]. O + já cria uma lista nova (aula Adicionando Itens).
Resumo
- Com listas,
b = anão copia: cria outro nome para a mesma lista. O que um muda, o outro vê. ispergunta se é a mesma lista;==pergunta se os itens são iguais.- Para copiar:
a.copy()(o mais claro),list(a)oua[:]. - Dar uma lista nova para a variável (
b = [...]) descola o nome; não muda a lista antiga. - As três cópias são rasas: listas dentro da lista continuam compartilhadas. Para isso existe a cópia profunda (Cópia Rasa e Cópia Profunda).
[[0, 0]] * 3repete a mesma lista três vezes; use[[0, 0] for i in range(3)].