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

Listas ​

Uma variável guarda uma coisa. Uma lista guarda várias coisas em ordem, todas sob um único nome. Um placar é uma sequência ordenada de pontuações. Um questionário é uma coleção de perguntas. No momento em que você precisa gerenciar um grupo de valores relacionados, você precisa de uma lista.

Listas são a sequência ordenada e mutável de uso geral do Python. Elas são a escolha natural para qualquer coisa que muda com o tempo: itens adicionados ou removidos, ordem embaralhada, conteúdo filtrado ou ordenado. Quando a ordem importa e a coleção muda, uma lista costuma ser a primeira opção certa.

Uma list é o array dinâmico do Python: uma sequência ordenada e mutável (que pode ser alterada no próprio lugar). O perfil de desempenho é o que orienta as decisões de design. Ler por índice é O(1), tempo constante independente do tamanho da lista (big-O é a notação para como o custo escala com o tamanho, n). append() também é efetivamente O(1): a lista mantém espaço extra no final e só é redimensionada ocasionalmente. Os custos que pesam são insert() e remove(), com O(n), porque todo elemento depois do ponto de mudança precisa deslocar de posição. Quando você se perceber inserindo ou removendo no início dentro de um loop, esse O(n) é o sinal para buscar 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 ponto de partida que você vai preenchendo com o tempo.

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

Um literal com colchetes 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 está de fato). Assim, um elemento pode ser qualquer objeto do Python, inclusive outra lista. Misturar tipos em uma mesma lista é válido, mas incomum fora de scripts rápidos: a maioria das listas que você mantém guarda um único tipo, o que facilita raciocinar sobre elas e processá-las em um loop.

python
scores = [87, 92, 74, 65, 91]
players = ["Ana", "Bruno", "Carlos"]
mixed = ["Ana", 87, True, 3.14]   # qualquer tipo, embora incomum
empty = []
JunoCriando uma lista Valores entre colchetes, separados por vírgulas, e o Python os mantém na ordem em que você escreveu. Um [] vazio é um bom ponto de partida quando você planeja preenchê-lo aos poucos. A maioria das minhas listas começa assim, vazias e esperando.
JunoCriando uma lista Colchetes definem uma lista e a ordem de inserção é preservada. Uma lista pode conter qualquer valor, inclusive outras listas, e [] é o ponto de partida padrão quando você acumula itens de forma incremental.
JunoCriando uma lista Uma lista guarda referências para seus elementos, então pode misturar qualquer tipo de objeto, embora manter um único tipo por lista seja o que você realmente quer. Comece com [] e vá construindo, a sobra de espaço alocada torna isso barato.

Indexação e fatiamento ​

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

A indexação e o fatiamento de listas seguem as mesmas regras das strings. A diferença principal é a mutabilidade: você pode atribuir a um índice ou a uma fatia para alterar itens no próprio lugar, algo que strings não permitem.

Indexação e fatiamento funcionam como em uma string, mas em uma lista você também pode atribuir valores, já que ela é mutável. O ponto importante é a atribuição por fatia: ela pode alterar o tamanho da lista. lst[1:3] = [10, 20, 30] substitui os dois itens nas posições 1 e 2 por três novos, aumentando a lista em um item. A substituição não precisa ter o mesmo tamanho da fatia original, o que é conveniente e uma surpresa comum, então use esse recurso de forma deliberada, não 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 gerariam TypeError)
JunoIndexação e fatiamento As posições começam em 0, e números negativos contam a partir do final, então scores[-1] é o último. Uma fatia como scores[1:3] entrega uma lista nova com as posições 1 e 2. A parte que me pegou de início: em uma lista você também pode escrever em uma posição, scores[0] = 90, algo que uma string recusaria.
JunoIndexação e fatiamento Mesma indexação e fatiamento que em strings, posições a partir de 0, negativos a partir do final. A diferença é a mutabilidade: atribua a um índice ou a uma fatia e você altera a lista no próprio lugar, onde uma string geraria TypeError.
JunoIndexação e fatiamento Funciona como a indexação de strings, mas você pode atribuir, e a atribuição por fatia 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 com intenção.

Adicionando itens ​

Três métodos para adicionar itens. append() adiciona um único item ao final e é o que você vai 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ó é redimensionada 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 os elementos subsequentes. extend() é equivalente a += e mais eficiente do que chamar append() repetidamente dentro de um loop.

append() é barato porque a lista mantém espaço extra e só é redimensionada de vez em quando, então o custo médio permanece O(1) (constante, independente do tamanho). insert(0, x) é a armadilha: inserir no início é O(n) porque cada elemento se desloca uma posição para a direita. Se você constrói uma lista adicionando repetidamente no início, isso vira O(n ao quadrado) no total. Quando precisar de inserção rápida no início, use collections.deque da biblioteca padrão, cujo appendleft é O(1), abordado no capítulo Módulos. extend() faz a lista crescer em uma única passada, e é por isso que supera um loop de chamadas a 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: usar append() com uma lista adiciona a lista inteira como um único item, criando uma lista dentro de outra lista. Use extend() para mesclar em vez disso:

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

append(x) adiciona x como um único elemento, seja o que for x, então passar uma lista faz com que ela seja aninhada por completo. extend(iterable) percorre o argumento e adiciona cada elemento separadamente. lst += other faz a mesma coisa que extend, então use o que for mais claro no ponto de chamada.

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

Removendo itens ​

Quatro ferramentas para remover itens. remove() busca por valor. pop() remove por posição e devolve o item removido. del remove por posição sem devolver nada. clear() esvazia a lista inteira.

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

remove(value) percorre a lista do início comparando cada elemento até encontrar uma correspondência, e então desloca tudo que vem depois para a esquerda: O(n) (o custo cresce com o tamanho da lista). pop() no final é O(1), nada se desloca. pop(i) em qualquer outro lugar é O(n) pelo mesmo motivo do deslocamento. Se você se perceber removendo itens de posições arbitrárias dentro de um loop crítico, esse O(n) repetido é o sinal para reestruturar os dados ou escolher outra coleção.

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

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

remove() gera um ValueError se o valor não estiver na lista. Verifique com in antes se não tiver certeza:

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

remove() gera ValueError quando não encontra o valor. A verificação com in adiciona uma varredura O(n) extra, então você percorre a lista duas vezes. Para código pontual isso não é problema. Quando você preferir capturar a falha em vez de verificar antes, try / except ValueError é abordado no capítulo Arquivos e exceções.

O padrão in seguido de remove() são duas varreduras O(n), a lista percorrida duas vezes. Quando a ordem não importa, trocar o item alvo com o último elemento e depois usar pop() é O(1) (custo constante, sem deslocamento). E se você testa a presença de itens com frequência, um set faz a busca em O(1) onde uma lista é O(n), abordado no capítulo Tuplas e conjuntos. A regra prática: uma lista para dados ordenados e mutáveis, um conjunto no momento em que "isso está aí?" for a pergunta frequente.

JunoRemovendo itensremove() remove por valor e apenas a primeira correspondência, e gera ValueError se o valor não estiver lá, então verifique com in antes quando não tiver certeza. pop() remove por posição e devolve o item, del remove por posição e não devolve nada.
JunoRemovendo itensremove() remove a primeira correspondência por valor e gera ValueError quando não encontra. pop() no final é barato e devolve o item; pop(i) em outro lugar desloca tudo que vem depois. del remove por posição, sem retorno.
JunoRemovendo itensremove() e pop(i) fora do final são deslocamentos O(n); só o pop() no final é O(1). Se "isso está aí?" for o caminho crítico, um set resolve em O(1) onde a lista é O(n). Verificar com in antes de remove() percorre a lista duas vezes.

Ordenação ​

sorted() devolve uma lista nova e ordenada e deixa a original intacta. .sort() ordena a lista no próprio lugar e devolve None. Essa diferença importa mais do que parece.

sorted() é a opção segura padrão: ela nunca modifica a original. .sort() modifica no próprio lugar e devolve None, o que é uma armadilha comum. Atribuir o resultado de .sort() te dá None, não a lista ordenada. Use sorted() quando precisar manter a original intacta; use .sort() quando só quiser a versão ordenada.

A ordenação do Python é estável: elementos iguais mantêm a ordem relativa original. É isso que permite ordenar por uma chave, depois ordenar novamente por outra, e a primeira ordenação sobrevive como critério de desempate. Ela também é rápida em dados quase ordenados, próxima de O(n) (linear) em vez do O(n log n) de uma ordenação do zero, então reordenar uma lista quase ordenada é barato. .sort() devolve None de propósito: um método que altera a lista no próprio lugar não também a devolve, e essa convenção é o que evita que você escreva x = lst.sort() e acabe silenciosamente com None. sorted() aceita qualquer iterável, não só listas, e sempre devolve uma lista nova.

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

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

result = scores.sort()             # result é None, não a lista ordenada
JunoOrdenaçãosorted() te dá uma lista nova e ordenada e deixa a original intacta. .sort() rearranja a própria lista e devolve None, então x = scores.sort() te deixa com None, não a lista. Eu caí nesse mesmo erro mais de uma vez até fixar.
JunoOrdenaçãosorted() é a opção segura padrão, ela nunca toca na original. .sort() ordena no próprio lugar e devolve None, então atribuir o resultado dela é a armadilha para lembrar. Escolha sorted() quando precisar manter a original intacta.
JunoOrdenação A ordenação é estável, então ordene por uma chave secundária primeiro, depois pela principal, e os empates mantêm a ordem anterior. Ela é quase linear em dados quase ordenados, então reordenar é barato. .sort() devolve None de propósito; sorted() aceita qualquer iterável e devolve uma lista nova.

Operações úteis ​

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

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

len(), sum(), min() e max() funcionam em qualquer iterável, não só em listas, então a mesma chamada lê uma lista, uma tupla ou um gerador. O detalhe de custo que importa: tanto in quanto .index() percorrem a lista do início ao fim, O(n) (o custo cresce com o tamanho), porque uma lista não tem um índice do seu conteúdo. Verificações de presença repetidas dentro de um loop são onde isso se acumula, e um set é a solução. Um detalhe traiçoeiro: sum() começa em 0, então ela soma números, não strings; para juntar strings use "".join(), abordado 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 superficial
scores.reverse()     # inverte no próprio lugar
JunoOperações úteislen(), sum(), min() e max() funcionam direto em uma lista, sem preparo. in pergunta se algo está presente, .count() conta quantas vezes, e .index() encontra a posição da primeira correspondência.
JunoOperações úteis As funções embutidas funcionam em qualquer lista, e as de sequência funcionam em qualquer iterável. in é uma varredura linear, então para testes de presença repetidos converta para um conjunto. .index() gera ValueError quando o valor não é encontrado.
JunoOperações úteislen(), sum(), min(), max() aceitam qualquer iterável, não só listas. in e .index() são ambos varreduras O(n), então uma verificação de presença crítica pede um set. E sum() começa em 0, então para strings use "".join().

Iterando ​

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

for item in list invoca o iterador da lista e o avança a cada passo. enumerate(iterable, start=0) envolve o iterador e produz pares (index, value). Usar enumerate() é mais limpo e menos sujeito a erro do que manter uma variável contadora.

enumerate() produz pares (index, value) conforme você itera, e é por isso que for i, item in enumerate(...) fica limpo: cada par se desempacota nos dois nomes. O argumento start desloca o contador apenas para exibição (então start=1 numera a partir de um) sem mudar de onde o loop realmente lê na lista. O motivo para preferi-lo a um contador feito à mão não é velocidade, é que um contador separado que você incrementa manualmente é mais uma coisa para esquecer de atualizar, e enumerate() não pode ficar fora de sincronia.

python
players = ["Ana", "Bruno", "Carlos"]

for player in players:
    print(player)

for i, player in enumerate(players, start=1):
    print(f"{i}. {player}")
# 1. Ana
# 2. Bruno
# 3. Carlos

for e enumerate() recebem tratamento completo no capítulo Controle de fluxo. Em resumo: for player in players executa uma vez por item, e enumerate() te dá tanto a posição quanto o valor em cada iteração.

JunoIterando Um loop for percorre a lista item por item, e o nome depois de for se torna esse item em cada volta. Quando você quer a posição também, enumerate() te dá as duas coisas, então você nunca precisa manter um contador sozinho. Deixar ele contar por mim me poupou vários erros de contagem.
JunoIterandofor item in list visita cada item em ordem. enumerate(iterable, start=0) produz pares (index, value), mais limpo e menos sujeito a erro do que um contador gerenciado à mão. Use start=1 quando quiser numeração a partir de um.
JunoIterandoenumerate() produz pares (index, value) que se desempacotam direto em dois nomes, e start só desloca o contador exibido, não a posição de leitura. A vantagem não é velocidade, é que um contador manual pode ficar fora de sincronia e esse 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 escolhe a linha, o segundo escolhe a coluna.

Listas aninhadas são listas de referências para outras listas. Cada lista interna é um objeto independente. Acesse com índices encadeados: grid[row][col]. Alterar uma lista interna afeta a lista externa porque a lista externa guarda uma referência ao mesmo objeto.

Uma lista aninhada não é um array 2D de verdade. A lista externa guarda referências para listas internas, cada uma seu próprio objeto independente, então as linhas podem ter tamanhos e tipos diferentes. grid[1][2] é duas buscas: escolhe a linha, depois indexa dentro dela. A consequência que pega a maioria das pessoas: uma cópia superficial da lista externa (do 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 aparece nas duas cópias. A seção sobre mutabilidade a seguir deixa 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, e é assim que você constrói uma grade ou tabela: uma lista de linhas, cada linha uma lista. Dois conjuntos de colchetes acessam o conteúdo, grid[1][2], o primeiro escolhe a linha, o segundo escolhe dentro dela.
JunoListas aninhadas Listas aninhadas são listas de referências para outras listas, cada lista interna seu próprio objeto. Acesse com índices encadeados, grid[row][col]. Como a lista externa só guarda referências, alterar uma lista interna aparece em todos os nomes que apontam para ela.
JunoListas aninhadas Não é um array 2D de verdade: a lista externa guarda referências, então as linhas internas podem variar em tamanho e tipo, e grid[1][2] são duas buscas. Uma cópia superficial duplica a lista externa, mas compartilha as internas, o que é a pegadinha que a próxima seção explora.

Mutabilidade: a pegadinha ​

Isso surpreende quase todo mundo. Atribuir uma lista a uma nova variável não cria uma cópia. Os dois nomes apontam para a mesma lista. Mude um e você muda o outro. Para ter uma cópia independente, você precisa pedir uma explicitamente.

A atribuição de lista copia a referência, não o objeto. Os dois nomes apontam para a mesma lista subjacente. Mudanças feitas por qualquer um dos nomes afetam os mesmos dados. Quando você precisar de dados independentes, copie explicitamente com .copy(), list() ou uma fatia completa [:].

b = a faz um segundo nome apontar para o mesmo objeto de lista, então qualquer mudança feita através de b também aparece no objeto que a vê. .copy() e a[:] fazem uma cópia superficial: uma nova lista externa contendo as mesmas referências de elementos que a original tinha. Para uma lista simples de valores imutáveis (números, strings, coisas que não podem mudar no próprio lugar) isso é tudo que você precisa. Para listas aninhadas isso é uma armadilha, porque as listas internas continuam compartilhadas entre a original e a cópia, então editar uma linha através de uma aparece na outra.

python
a = [1, 2, 3]
b = a            # b não é uma cópia; ele 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]]  (a lista interna era compartilhada)

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

JunoMutabilidade: a pegadinhab = a não cria uma cópia, os dois nomes apontam para a mesma lista, então uma mudança feita por um aparece através do outro. Para uma lista independente você precisa pedir: .copy(), list(a), ou a[:]. Isso surpreende quase todo mundo na primeira vez, eu incluído.
JunoMutabilidade: a pegadinha A atribuição copia a referência, não a lista, então b = a deixa dois nomes em um único objeto e mudanças feitas por qualquer um afetam os mesmos dados. Copie explicitamente com .copy(), list(a), ou a[:] quando precisar de independência.
JunoMutabilidade: a pegadinhab = a compartilha um único objeto; .copy() e a[:] fazem uma cópia superficial, uma nova lista externa sobre as mesmas referências internas. Listas simples de valores imutáveis ficam seguras assim, as aninhadas compartilham suas linhas, então use copy.deepcopy() quando precisar que o interior também seja independente.

Mais métodos ​

MétodoO que faz
.append(item)Adiciona ao final
.insert(i, item)Insere na posição i
.extend(iterable)Adiciona todos os itens de um iterável
.remove(value)Remove a primeira ocorrência do valor
.pop(i)Remove e devolve o item na posição i (padrão: 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 próprio lugar
.reverse()Inverte no próprio lugar
.copy()Devolve uma cópia superficial

Na prática ​

Construindo um rastreador de pontuações: 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 ordenadas: {scores}")
print(f"Maior: {scores[0]}")
print(f"Menor: {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 os resultados ordenados.

python
names = ["Ana", "Bruno", "Carla", "Davi"]
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 (ordenada): {ranked}")

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

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

python
# Alias: 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 superficial: a lista externa é independente, as 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 for (sem precisar de imports)
matrix = [[1, 2, 3], [4, 5, 6]]
deep_copy = []
for row in matrix:
    deep_copy.append(row[:])    # copia cada lista interna explicitamente

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