Skip to content
This page has been auto-translated and may contain errors.View in English

Listas

docs.scrimba.com

Uma variável guarda uma coisa. Uma lista guarda muitas coisas em ordem, todas sob um único nome. Um ranking é uma sequência ordenada de pontuações. Um quiz é uma coleção de perguntas. Quando você precisa gerenciar um grupo de valores relacionados, você precisa de uma lista.

Listas são a sequência ordenada, mutável de propósito geral do Python. Elas são a escolha natural para qualquer coisa que muda ao longo do tempo: itens adicionados ou removidos, ordem embaralhada, conteúdo filtrado ou ordenado. Quando a ordem importa e a coleção muda, uma lista é geralmente a primeira escolha certa.

Uma list é o array dinâmico do Python: uma sequência ordenada, mutável (alterável no local). O perfil de desempenho é o que impulsiona as decisões de design. Ler por índice é O(1), tempo constante não importa o quão grande a lista seja (big-O é a abreviação de como o custo escala com o tamanho, n). append() é efetivamente O(1) também: a lista mantém espaço extra no final e só redimensiona ocasionalmente. Os custos que mordem são insert() e remove() em O(n), porque cada elemento após o ponto de mudança se desloca. Quando você se vê inserindo ou removendo na frente em um loop, esse O(n) é o sinal para usar uma estrutura diferente.

Criando uma lista

Colchetes, valores separados por vírgulas. Listas podem conter qualquer mistura de tipos, e uma lista vazia é válida e comum como um ponto de partida que você constrói ao longo do tempo.

Listas são definidas com sintaxe de colchetes e preservam a ordem de inserção. Elas podem conter qualquer valor Python, incluindo outras listas. A lista vazia [] é o ponto de partida padrão quando você acumula itens incrementalmente.

Um literal de colchete cria um novo objeto list, e a lista armazena referências para seus elementos, não os valores copiados (uma referência é um ponteiro para onde o objeto real vive). Então um elemento pode ser qualquer objeto Python, incluindo outra lista. Misturar tipos em uma lista é válido mas incomum fora de scripts rápidos: a maioria das listas que você mantém contém um tipo, o que as torna mais fáceis de raciocinar e processar em um loop.

python
scores = [87, 92, 74, 65, 91]
players = ["Maria", "João", "Carlos"]
mixed = ["Maria", 87, True, 3.14]   # qualquer tipo, embora incomum
empty = []
JunoCriando uma lista Valores entre colchetes, separados por vírgulas, e Python os mantém na ordem em que você os escreveu. Um [] vazio é um bom ponto de partida quando você planeja preenchê-lo conforme avança. A maioria das minhas listas começa assim, vazia e esperando.
JunoCriando uma lista Colchetes definem uma lista e a ordem de inserção é preservada. Uma lista pode conter qualquer valor, incluindo outras listas, e [] é o início padrão quando você acumula itens incrementalmente.
JunoCriando uma lista Uma lista mantém referências para seus elementos, então pode misturar qualquer objeto, embora um tipo por lista seja o que você realmente deseja manter. Use [] e construa, a alocação excessiva torna isso barato.

Indexação e fatiamento

Listas usam a mesma numeração que strings: posições começam em 0, números negativos contam a partir do final. Você lê qualquer item por sua posição. Como listas são mutáveis, você também pode escrever em uma posição específica.

Indexação e fatiamento de listas seguem as mesmas regras que strings. A diferença principal é mutabilidade: você pode atribuir a um índice ou um fatiamento para alterar itens no local, algo que strings não permitem.

Indexação e fatiamento leem igual a em uma string, mas uma lista permite que você atribua também, já que é mutável. O que saber é atribuição de fatiamento: pode mudar o comprimento da lista. lst[1:3] = [10, 20, 30] substitui os dois itens nas posições 1 e 2 por três novos, crescendo a lista em um. A substituição não precisa corresponder ao comprimento do fatiamento, o que é conveniente e uma surpresa comum, então use-o deliberadamente em vez de por acidente.

python
scores = [87, 92, 74, 65, 91]

scores[0]      # 87  (primeiro)
scores[-1]     # 91  (último)
scores[1:3]    # [92, 74]
scores[:2]     # [87, 92]
scores[::-1]   # [91, 65, 74, 92, 87]  (invertido)

scores[0] = 90   # mutável: funciona (strings levantariam TypeError)
JunoIndexação e fatiamento Posições começam em 0, e números negativos contam de trás para frente a partir do final, então scores[-1] é o último. Um fatiamento como scores[1:3] lhe entrega uma lista fresca com posições 1 e 2. A parte que me levou um momento: com uma lista você também pode escrever em uma posição, scores[0] = 90, onde uma string se recusaria.
JunoIndexação e fatiamento A mesma indexação e fatiamento que strings, posições de 0, negativos do final. A diferença é mutabilidade: atribua a um índice ou fatiamento e você altera a lista no local, onde uma string levantaria TypeError.
JunoIndexação e fatiamento Lê como indexação de string, mas você pode atribuir, e atribuição de fatiamento pode redimensionar a lista. lst[1:3] = [10, 20, 30] troca dois itens por três, útil de propósito e uma surpresa por acidente, então use-o deliberadamente.

Adicionando itens

Três métodos para adicionar itens. append() adiciona um único item no final e é o que você usará quase sempre. insert() adiciona em uma posição específica. extend() mescla outra lista.

append() é O(1) amortizado (a lista mantém espaço extra e só redimensiona ocasionalmente, então o custo médio permanece constante) e é a forma padrão de construir uma lista item por item. insert() é O(n) porque desloca elementos subsequentes. extend() é equivalente a += e mais eficiente que repetidos append() dentro de um loop.

append() é barato porque a lista mantém espaço extra e só redimensiona de vez em quando, então o custo médio permanece O(1) (constante, não importa o comprimento). insert(0, x) é a armadilha: inserir na frente é O(n) porque cada elemento se move uma casa para a direita. Se você constrói uma lista adicionando repetidamente na frente, isso se torna O(n ao quadrado) no geral. Quando você precisa de inserção rápida na frente, use collections.deque da biblioteca padrão, cujo appendleft é O(1), coberto no capítulo Módulos. extend() cresce a lista em uma passada, é por isso que supera um loop de chamadas append() quando você já tem os itens.

python
scores = [87, 92, 74]

scores.append(65)          # [87, 92, 74, 65]
scores.insert(1, 100)      # [87, 100, 92, 74, 65]
scores.extend([55, 71])    # [87, 100, 92, 74, 65, 55, 71]

Um erro comum: append() com uma lista adiciona a lista inteira como um item, dando-lhe uma lista dentro de uma lista. Use extend() para mesclar em vez disso:

append(x) sempre adiciona x como um elemento único. Passar uma lista para append() lhe dá uma lista aninhada. Use extend() quando quiser mesclar todos os itens de outra lista nesta:

append(x) adiciona x como um elemento não importa o que x seja, então entregar-lhe uma lista aninha essa lista inteira. extend(iterable) caminha pelo argumento e adiciona cada elemento separadamente. lst += other faz a mesma coisa que extend, então use aquele que lê mais claro no local da chamada.

python
scores.append([55, 71])    # [..., [55, 71]]  lista aninhada, provavelmente errado
scores.extend([55, 71])    # [..., 55, 71]    mesclado, correto
JunoAdicionando itensappend() coloca um item no final e é o que você praticamente sempre usará. insert() coloca um item em uma posição, extend() mescla outra lista. O deslize clássico: append() uma lista e você obtém uma lista dentro de sua lista, então use extend() quando quiser mesclar.
JunoAdicionando itensappend() é seu padrão, item por item no final. insert() desloca tudo depois dele, então custa mais. append() uma lista e ela se aninha; extend() (ou +=) mescla os itens em vez disso.
JunoAdicionando itensappend() é O(1) amortizado, insert(0, x) é O(n), então construir da frente para trás em um loop silenciosamente fica quadrático, use deque ali. E append() uma lista a aninha inteira; extend() ou += é a mesclagem.

Removendo itens

Quatro ferramentas para remover itens. remove() busca por valor. pop() remove por posição e lhe entrega o item. del remove por posição sem um valor de retorno. clear() esvazia a lista inteira.

remove() é O(n): ela varre procurando a primeira ocorrência por valor. pop() sem um argumento é O(1) para o último item. pop(i) para qualquer outra posição é O(n) porque elementos se deslocam. del scores[i] é equivalente a pop(i) mas descarta o valor de retorno.

remove(value) varre da frente comparando cada elemento até encontrar uma correspondência, então desloca tudo depois dela para a esquerda: O(n) (o custo cresce com o comprimento da lista). pop() do final é O(1), nada se desloca. pop(i) em qualquer outro lugar é O(n) pela mesma razão de deslocamento. Se você se vê removendo de posições arbitrárias em um loop crítico, esse O(n) repetido é o sinal para reestruturar os dados ou escolher uma coleção diferente.

python
scores = [87, 92, 74, 65, 91]

scores.remove(74)    # remove primeira ocorrência de 74
scores.pop()         # remove e retorna último item (91)
scores.pop(0)        # remove e retorna item na posição 0 (87)
del scores[1]        # remove na posição 1, sem valor de retorno
scores.clear()       # remove tudo

remove() levanta ValueError se o valor não estiver na lista. Verifique com in primeiro se você não tem certeza:

python
if 74 in scores:
    scores.remove(74)

remove() levanta ValueError em uma falha. A verificação in adiciona uma varredura O(n) extra, então você caminha pela lista duas vezes. Para código único tudo bem. Quando você preferiria capturar a falha em vez de pré-verificar, try / except ValueError é coberto no capítulo Arquivos e exceções.

O padrão in depois remove() é duas varreduras O(n), a lista caminhada duas vezes. Quando a ordem não importa, trocar o alvo com o último elemento e fazer pop dele é O(1) (custo constante, sem deslocamentos). E se você testa pertencimento muito, um set procura em O(1) onde uma lista é O(n), coberto no capítulo Tuplas e conjuntos. A regra de ouro: uma lista para dados ordenados e mutáveis, um conjunto no momento em que "está aí" é a pergunta quente.

JunoRemovendo itensremove() deleta por valor e apenas a primeira correspondência, e levanta ValueError se o valor não estiver lá, então verifique com in primeiro quando não tem certeza. pop() remove por posição e entrega o item, del remove por posição e não lhe entrega nada.
JunoRemovendo itensremove() deleta a primeira correspondência de valor e levanta ValueError em uma falha. pop() do final é barato e retorna o item; pop(i) em outro lugar desloca tudo depois dele. del remove por posição, sem retorno.
JunoRemovendo itensremove() e pop(i) longe do final são deslocamentos O(n); apenas pop() da cauda é O(1). Se "está aí" é o caminho crítico, um set faz em O(1) onde a lista é O(n). Pré-verificar com in antes de remove() caminha pela lista duas vezes.

Ordenação

sorted() retorna uma lista completamente nova e ordenada e deixa sua original intocada. .sort() ordena a lista no lugar e retorna None. Essa diferença importa mais do que parece.

sorted() é o padrão seguro: nunca modifica o original. .sort() modifica no lugar e retorna None, que é uma armadilha comum. Atribuir o resultado de .sort() lhe dá None, não a lista ordenada. Use sorted() quando precisar do original intacto; use .sort() quando quiser apenas a versão ordenada.

A ordenação do Python é estável: elementos iguais mantêm sua ordem relativa original. Isso é o que permite ordenar por uma chave, depois ordenar novamente por outra, e ter a primeira ordenação sobreviver como um desempatador. Também é rápida em dados quase-ordenados, perto de O(n) (linear) em vez do O(n log n) de uma ordenação do zero, então re-ordenar uma lista majoritariamente ordenada é barato. .sort() retorna None de propósito: um método que muda a lista no lugar não também a retorna, que é a convenção que o impede de escrever x = lst.sort() e silenciosamente obter None. sorted() toma qualquer iterável, não apenas listas, e sempre retorna uma lista fresca.

python
scores = [87, 42, 96, 55, 71]

ranked = sorted(scores)            # [42, 55, 71, 87, 96] (nova lista)
scores.sort()                      # ordena no lugar, retorna None
scores.sort(reverse=True)          # [96, 87, 71, 55, 42]

result = scores.sort()             # result é None, não a lista ordenada
JunoOrdenaçãosorted() lhe dá uma nova lista ordenada e deixa o original sozinho. .sort() reorganiza a lista em si e retorna None, então x = scores.sort() lhe deixa com None, não a lista. Cometi exatamente esse erro mais de uma vez antes de ficar.
JunoOrdenaçãosorted() é o padrão seguro, nunca toca o original. .sort() ordena no lugar e retorna None, então atribuir seu resultado é a armadilha a lembrar. Escolha sorted() quando precisar do original intacto.
JunoOrdenação A ordenação é estável, então ordene por uma chave secundária primeiro, depois a primária, e empates mantêm sua ordem anterior. Ele é executado perto de linear em dados quase-ordenados, então re-ordenar é barato. .sort() retorna None por design; sorted() toma qualquer iterável e retorna uma lista nova.

Operações úteis

Python tem um conjunto de ferramentas integradas que funcionam diretamente em listas. len(), sum(), min() e max() são as quatro que você usará constantemente.

As funções de sequência integradas funcionam em qualquer lista. in é uma varredura linear para listas; se você precisar de testes de pertencimento rápidos repetidos, converta para um conjunto. .index() levanta ValueError se o valor não for encontrado.

len(), sum(), min() e max() funcionam em qualquer iterável, não apenas listas, então a mesma chamada lê uma lista, uma tupla ou um gerador. A nota de custo que importa: in e .index() ambas varrem da frente para trás, O(n) (o custo cresce com o comprimento), porque uma lista não tem um índice de seus conteúdos. Verificações de pertencimento repetidas em um loop são onde isso soma, e um set é a solução. Uma aresta aguçada: sum() começa de 0, então adiciona números, não strings; para juntar strings use "".join(), coberto no capítulo Strings.

python
scores = [87, 92, 74, 65, 91]

len(scores)          # 5
sum(scores)          # 409
min(scores)          # 65
max(scores)          # 92
scores.count(87)     # 1
scores.index(74)     # 2
74 in scores         # True
74 not in scores     # False
scores.copy()        # cópia rasa
scores.reverse()     # inverte no lugar
JunoOperações úteislen(), sum(), min() e max() todos funcionam direto em uma lista, sem setup. in pergunta se algo está presente, .count() totaliza quantas vezes, e .index() encontra a posição da primeira correspondência.
JunoOperações úteis As funções integradas funcionam em qualquer lista, e as de sequência funcionam em qualquer iterável. in é uma varredura linear, então para testes de pertencimento repetidos converta para um conjunto. .index() levanta ValueError quando o valor não é encontrado.
JunoOperações úteislen(), sum(), min(), max() tomam qualquer iterável, não apenas listas. in e .index() são ambas varreduras O(n), então uma verificação de pertencimento crítica quer um set. E sum() começa de 0, então para strings alcance "".join().

Iterando

Um loop for passa por uma lista um item por vez. A variável depois de for recebe cada item por vez. Quando você também precisa da posição, enumerate() lhe dá ambas sem um contador manual.

for item in list invoca o iterador da lista e o avança em cada passo. enumerate(iterable, start=0) envolve o iterador e cede pares (index, value). Usar enumerate() é mais limpo e menos propenso a erros que manter uma variável contador.

enumerate() cede pares (index, value) conforme você faz loop, é por isso que for i, item in enumerate(...) lê limpo: cada par se desempacota nos dois nomes. O argumento start desloca apenas o contador para exibição (então start=1 numera de um) sem mudar aonde o loop realmente lê na lista. A razão para preferir em vez de um contador feito à mão não é velocidade, é que um contador separado que você incrementa à mão é uma coisa a mais para esquecer de incrementar, e enumerate() não pode sair de sincronização.

python
players = ["Maria", "João", "Carlos"]

for player in players:
    print(player)

for i, player in enumerate(players, start=1):
    print(f"{i}. {player}")
# 1. Maria
# 2. João
# 3. Carlos

for e enumerate() recebem tratamento completo no capítulo Fluxo de controle. A versão curta: for player in players é executado uma vez por item, e enumerate() lhe dá tanto a posição quanto o valor a cada iteração.

JunoIterando Um loop for caminha pela lista um item por vez, e o nome depois de for se torna esse item cada vez. Quando quer a posição também, enumerate() lhe entrega ambas, então você nunca precisa manter um contador você mesmo. Deixá-lo contar para mim me salvou de um monte de erros off-by-one.
JunoIterandofor item in list visita cada item em ordem. enumerate(iterable, start=0) cede pares (index, value), mais limpo e menos propenso a erros que um contador que você gerencia à mão. Use start=1 quando quiser numeração baseada em um.
JunoIterandoenumerate() cede pares (index, value) que se desempacotam direto em dois nomes, e start apenas desloca o contador exibido, não a posição de leitura. O ganho não é velocidade, é que um contador manual pode sair de sincronização e este não pode.

Listas aninhadas

Uma lista pode conter outras listas. É assim que você representa uma grade ou uma tabela: uma lista de linhas, cada linha sendo uma lista de valores. Dois conjuntos de colchetes acessam um item: o primeiro pega a linha, o segundo pega a coluna.

Listas aninhadas são listas de referências de lista. Cada lista interna é um objeto independente. Acesse com subscritos encadeados: grid[row][col]. Mutar uma lista interna afeta a lista externa porque a lista externa mantém uma referência ao mesmo objeto.

Uma lista aninhada não é um verdadeiro array 2D. A lista externa mantém referências para listas internas, cada uma seu próprio objeto independente, então linhas podem ter comprimentos diferentes e conter tipos diferentes. grid[1][2] são duas procuras: pegar a linha, depois indexar nela. A consequência que pega as pessoas: uma cópia rasa da lista externa (o tipo que .copy() faz) duplica o contêiner externo mas ainda aponta para as mesmas listas internas, então uma mudança em uma linha interna mostra em ambas as cópias. A seção de mutabilidade abaixo torna isso concreto.

python
grid = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]

grid[0]       # [1, 2, 3]
grid[1][2]    # 6  (linha 1, coluna 2)
JunoListas aninhadas Uma lista pode conter outras listas, que é como você constrói uma grade ou tabela: uma lista de linhas, cada linha uma lista. Dois conjuntos de colchetes chegam dentro, grid[1][2], o primeiro pega a linha, o segundo pega dela.
JunoListas aninhadas Listas aninhadas são listas de referências para outras listas, cada lista interna seu próprio objeto. Chegue dentro com subscritos encadeados, grid[row][col]. Porque a lista externa apenas mantém referências, mutar uma lista interna mostra através de cada nome apontando para ela.
JunoListas aninhadas Não é um verdadeiro array 2D: a lista externa mantém referências, então linhas internas podem variar em comprimento e tipo, e grid[1][2] são duas procuras. Uma cópia rasa duplica a lista externa mas compartilha as internas, que é a armadilha que a próxima seção cai.

Mutabilidade: a armadilha

Isso surpreende quase todos. Atribuir uma lista a uma nova variável não faz uma cópia. Ambos os nomes apontam para a mesma lista. Mude um e você muda o outro. Para obter uma cópia independente, você tem que pedir explicitamente.

Atribuição de lista copia a referência, não o objeto. Ambos os nomes apontam para o mesmo objeto de lista subjacente. Mutações através de qualquer nome afetam os mesmos dados. Quando você precisa de dados independentes, copie explicitamente com .copy(), list() ou um fatiamento completo [:].

b = a aponta um segundo nome para o mesmo objeto de lista, então qualquer mudança através de b cai no objeto que a também vê. .copy() e a[:] fazem uma cópia rasa: uma nova lista externa mantendo as mesmas referências de elemento que o original fez. Para uma lista plana de valores imutáveis (números, strings, coisas que não podem mudar no lugar) é tudo que você precisa. Para listas aninhadas é uma armadilha, porque as listas internas ainda são compartilhadas entre o original e a cópia, então editar uma linha através de uma mostra na outra.

python
a = [1, 2, 3]
b = a            # b não é uma cópia; aponta para a mesma lista

b.append(4)
print(a)         # [1, 2, 3, 4]  (mudou: a e b são a mesma lista)
python
b = a.copy()    # cópia independente
b = list(a)     # mesmo resultado
b = a[:]        # também o mesmo

# Listas aninhadas ainda compartilham seus objetos internos:
matrix = [[1, 2], [3, 4]]
copy = matrix.copy()

copy[0].append(99)
print(matrix)   # [[1, 2, 99], [3, 4]]  (lista interna foi compartilhada)

Para estruturas aninhadas onde você precisa de independência completa, copie cada lista interna manualmente ou use copy.deepcopy() da biblioteca padrão, coberto no capítulo Módulos.

JunoMutabilidade: a armadilhab = a não faz uma cópia, ambos os nomes apontam para a mesma lista, então uma mudança através de um mostra através da outra. Para uma lista independente você tem que pedir: .copy(), list(a) ou a[:]. Este pega quase todos de primeira, eu incluído.
JunoMutabilidade: a armadilha Atribuição copia a referência, não a lista, então b = a deixa dois nomes em um objeto e mutações através de qualquer um acertam os mesmos dados. Copie explicitamente com .copy(), list(a) ou a[:] quando precisa de independência.
JunoMutabilidade: a armadilhab = a compartilha um objeto; .copy() e a[:] fazem uma cópia rasa, uma nova lista externa sobre as mesmas referências internas. Listas planas de valores imutáveis são seguras assim, listas aninhadas compartilham suas linhas, então alcance copy.deepcopy() quando precisa que o interior seja independente também.

Mais métodos

MétodoO que faz
.append(item)Adiciona no final
.insert(i, item)Insere na posição i
.extend(iterable)Adiciona todos os itens de um iterável
.remove(value)Remove primeira ocorrência do valor
.pop(i)Remove e retorna item na posição i (padrão: último)
.clear()Remove todos os itens
.index(value)Posição da primeira ocorrência
.count(value)Número de ocorrências
.sort()Ordena no lugar
.reverse()Inverte no lugar
.copy()Retorna uma cópia rasa

Na prática

Construindo um rastreador de pontuação: adicione resultados, ordene-os e imprima um resumo.

python
scores = []

scores.append(87)
scores.append(54)
scores.append(92)
scores.append(67)
scores.append(45)

scores.sort(reverse=True)

print(f"Pontuações classificadas: {scores}")
print(f"Mais alta: {scores[0]}")
print(f"Mais baixa:  {scores[-1]}")
print(f"Média: {sum(scores) / len(scores):.1f}")
print(f"Top 3:   {scores[:3]}")

Duas listas paralelas de nomes e pontuações: encontre o melhor desempenho e imprima resultados classificados.

python
names = ["Maria", "João", "Carolina", "David"]
scores = [87, 74, 92, 55]

best_score = max(scores)
best_index = scores.index(best_score)
best_player = names[best_index]

print(f"Melhor jogador: {best_player} ({best_score})")
print(f"Média:    {sum(scores) / len(scores):.1f}")

ranked = sorted(scores, reverse=True)
print(f"Distribuição (classificada): {ranked}")

for rank, score in enumerate(ranked, start=1):
    print(f"  Classificação {rank}: {score}")

Demonstrando a diferença entre aliasing e cópia, e entre cópias rasas e profundas de listas aninhadas.

python
# Aliasing: b não é uma cópia
a = [1, 2, 3]
b = a
b.append(4)
print(a)    # [1, 2, 3, 4]  (mesmo objeto)

# Cópia rasa: lista externa é independente, listas internas são compartilhadas
matrix = [[1, 2, 3], [4, 5, 6]]
shallow = matrix.copy()
shallow[0].append(99)
print(matrix)    # [[1, 2, 3, 99], [4, 5, 6]]  (lista interna compartilhada)

# Cópia profunda manual com um loop (sem imports necessários)
matrix = [[1, 2, 3], [4, 5, 6]]
deep_copy = []
for row in matrix:
    deep_copy.append(row[:])    # copie cada lista interna explicitamente

deep_copy[0].append(99)
print(matrix)    # [[1, 2, 3], [4, 5, 6]]  (inalterado)