Python & AI Tutorials Logo
Programação Python

14. Listas: Coleções Ordenadas de Itens

Até aqui neste livro, trabalhamos com partes individuais de dados: números únicos, strings e valores booleanos. Mas programas reais muitas vezes precisam trabalhar com coleções de itens relacionados — uma lista de nomes de alunos, uma série de leituras de temperatura, uma coleção de preços de produtos ou uma sequência de comandos do usuário. A lista(list) do Python é a ferramenta fundamental para armazenar e trabalhar com coleções ordenadas de dados.

Uma lista é uma sequência(sequence) que pode conter vários itens em uma ordem específica. Diferente de strings (que só podem conter caracteres), listas podem conter qualquer tipo de dado: números, strings, booleanos ou até outras listas. Listas também são mutáveis(mutable), o que significa que você pode mudar o conteúdo delas depois de criadas — adicionando itens, removendo itens ou modificando os que já existem.

Neste capítulo, vamos explorar como criar listas, acessar seus elementos, modificá-las e usá-las para resolver problemas práticos de programação. No final, você vai entender por que listas são uma das estruturas de dados mais poderosas e mais usadas no Python.

14.1) Criando Listas e Acessando Elementos

14.1.1) Criando Listas com Colchetes

A forma mais comum de criar uma lista é colocando os itens entre colchetes [], com os itens separados por vírgulas. Aqui vai um exemplo simples:

python
# Uma lista de nomes de alunos
students = ["Alice", "Bob", "Charlie", "Diana"]
print(students)  # Output: ['Alice', 'Bob', 'Charlie', 'Diana']

Repare como o Python exibe a lista: ele mostra os colchetes e coloca aspas em volta de cada string. Isso é a representação(representation) da lista — como o Python te mostra o que tem dentro.

Listas podem conter qualquer tipo de dado. Aqui vai uma lista de notas de prova:

python
# Uma lista de notas inteiras
scores = [85, 92, 78, 95, 88]
print(scores)  # Output: [85, 92, 78, 95, 88]

Você pode até misturar tipos diferentes na mesma lista, embora isso seja menos comum na prática:

python
# Uma lista com tipos mistos (menos comum, mas válida)
mixed_data = ["Alice", 25, True, 3.14]
print(mixed_data)  # Output: ['Alice', 25, True, 3.14]

Uma lista vazia(empty list) não contém itens e é criada só com os colchetes:

python
# Uma lista vazia
empty = []
print(empty)  # Output: []
print(len(empty))  # Output: 0

A função len(), que usamos com strings, também funciona com listas — ela retorna o número de itens na lista.

14.1.2) Entendendo a Ordem e as Posições na Lista

Listas mantêm a ordem(order) em que você adiciona itens. O primeiro item que você coloca continua sendo o primeiro, o segundo continua sendo o segundo, e assim por diante. Essa ordenação é crucial porque permite que você acesse itens específicos pela posição (também chamada de índice(index)).

O Python usa indexação baseada em zero(zero-based indexing): o primeiro item está na posição 0, o segundo na posição 1, e assim por diante. Isso pode parecer estranho no começo, mas é uma convenção usada por muitas linguagens de programação.

Lista: ['Alice', 'Bob', 'Charlie', 'Diana']

Índice 0: 'Alice'

Índice 1: 'Bob'

Índice 2: 'Charlie'

Índice 3: 'Diana'

Vamos ver como isso funciona na prática:

python
students = ["Alice", "Bob", "Charlie", "Diana"]
 
# Acesse o primeiro aluno (índice 0)
first_student = students[0]
print(first_student)  # Output: Alice
 
# Acesse o terceiro aluno (índice 2)
third_student = students[2]
print(third_student)  # Output: Charlie

Repare que, para pegar o terceiro aluno, usamos o índice 2, não 3. Isso acontece porque a contagem começa em 0.

14.1.3) Acessando Elementos com Índices Positivos

Para acessar um elemento de uma lista, escreva o nome da lista seguido do índice entre colchetes: list_name[index]. O índice precisa ser um inteiro dentro do intervalo válido (0 até len(list) - 1).

Aqui vai um exemplo prático trabalhando com preços de produtos:

python
# Preços de produtos em dólares
prices = [19.99, 24.50, 15.75, 32.00, 8.99]
 
# Acesse preços específicos
first_price = prices[0]
last_index = len(prices) - 1  # Calcular o último índice válido
last_price = prices[last_index]
 
print(f"First product costs: ${first_price}")  # Output: First product costs: $19.99
print(f"Last product costs: ${last_price}")    # Output: Last product costs: $8.99

Por que usamos len(prices) - 1 para o último índice? Porque se uma lista tem 5 itens, os índices são 0, 1, 2, 3, 4 — o último índice válido é sempre um a menos do que o comprimento.

Você também pode usar índices em expressões e cálculos:

python
scores = [85, 92, 78, 95, 88]
 
# Calcular a média das três primeiras notas
first_three_average = (scores[0] + scores[1] + scores[2]) / 3
print(f"Average of first three: {first_three_average}")  # Output: Average of first three: 85.0

14.1.4) Índices Negativos: Contando a Partir do Final

O Python oferece um recurso conveniente: índices negativos(negative indices) permitem acessar itens a partir do final da lista. O índice -1 se refere ao último item, -2 ao penúltimo, e assim por diante.

python
students = ["Alice", "Bob", "Charlie", "Diana"]
 
# Acessar a partir do final
last_student = students[-1]
second_to_last = students[-2]
 
print(last_student)      # Output: Diana
print(second_to_last)    # Output: Charlie

Isso é especialmente útil quando você quer o último item, mas não quer calcular len(list) - 1:

python
prices = [19.99, 24.50, 15.75, 32.00, 8.99]
 
# Essas duas abordagens são equivalentes
last_price_method1 = prices[len(prices) - 1]
last_price_method2 = prices[-1]
 
print(last_price_method1)  # Output: 8.99
print(last_price_method2)  # Output: 8.99

Veja como índices positivos e negativos se mapeiam para os mesmos itens:

Lista: ['Alice', 'Bob', 'Charlie', 'Diana']

Positivos: 0, 1, 2, 3

Negativos: -4, -3, -2, -1

Ambos se referem aos mesmos itens

14.1.5) O que Acontece com Índices Inválidos

Se você tentar acessar um índice que não existe, o Python levanta um IndexError:

python
students = ["Alice", "Bob", "Charlie"]
 
# AVISO: Esta lista tem índices 0, 1, 2 (ou -3, -2, -1) - apenas para demonstração
# Tentar acessar o índice 3 causa um erro
# PROBLEMA: O índice 3 não existe em uma lista com 3 itens
# print(students[3])  # IndexError: list index out of range

Esse erro é a forma do Python te dizer que você pediu um item que não está lá.

14.2) Indexação e Fatiamento de Listas

14.2.1) Entendendo o Básico do Fatiamento de Listas

Assim como podemos fatiar strings (como aprendemos no Capítulo 5), podemos fatiar(slice) listas para extrair partes delas. Um fatiamento cria uma nova lista contendo um subconjunto dos elementos da lista original. A sintaxe é list[start:stop], em que start é o índice onde o fatiamento começa (inclusivo) e stop é onde ele termina (exclusivo).

python
numbers = [10, 20, 30, 40, 50, 60, 70]
 
# Pegar elementos do índice 1 até (mas sem incluir) o índice 4
subset = numbers[1:4]
print(subset)  # Output: [20, 30, 40]

O fatiamento [1:4] inclui os índices 1, 2 e 3, mas para antes do índice 4. Essa regra de "stop é exclusivo" é a mesma do fatiamento de strings.

Vamos ver um exemplo prático com nomes de alunos:

python
students = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank"]
 
# Pegar os três primeiros alunos
first_three = students[0:3]
print(first_three)  # Output: ['Alice', 'Bob', 'Charlie']
 
# Pegar alunos do índice 2 ao 4
middle_group = students[2:5]
print(middle_group)  # Output: ['Charlie', 'Diana', 'Eve']

14.2.2) Omitindo Start ou Stop em Fatias

Você pode omitir o índice inicial para fatiar desde o começo, ou omitir o índice final para fatiar até o fim:

python
scores = [85, 92, 78, 95, 88, 91, 87]
 
# Do começo até o índice 3
first_few = scores[:3]
print(first_few)  # Output: [85, 92, 78]
 
# Do índice 4 até o fim
last_few = scores[4:]
print(last_few)  # Output: [88, 91, 87]
 
# A lista inteira (do começo ao fim)
all_scores = scores[:]
print(all_scores)  # Output: [85, 92, 78, 95, 88, 91, 87]

A fatia [:] cria uma cópia(copy) da lista inteira. Isso é útil quando você quer trabalhar com uma duplicata sem modificar a original — vamos explorar isso mais na seção 14.6.

14.2.3) Usando Índices Negativos em Fatias

Índices negativos funcionam em fatias do mesmo jeito que funcionam ao acessar um elemento único. Isso é especialmente útil para pegar itens do final:

python
students = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank"]
 
# Pegar os três últimos alunos
last_three = students[-3:]
print(last_three)  # Output: ['Diana', 'Eve', 'Frank']
 
# Pegar todos, exceto os dois últimos alunos
all_but_last_two = students[:-2]
print(all_but_last_two)  # Output: ['Alice', 'Bob', 'Charlie', 'Diana']
 
# Pegar do terceiro a partir do fim até o penúltimo
middle_from_end = students[-3:-1]
print(middle_from_end)  # Output: ['Diana', 'Eve']

14.2.4) Fatiamento com um Valor de Step

Você pode adicionar um terceiro parâmetro para controlar o passo(step) (quantos índices pular entre itens). A sintaxe completa é list[start:stop:step]:

python
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
 
# A cada segundo número começando do índice 0
evens = numbers[0:10:2]
print(evens)  # Output: [0, 2, 4, 6, 8]
 
# A cada terceiro número começando do índice 1
every_third = numbers[1:10:3]
print(every_third)  # Output: [1, 4, 7]

Você também pode usar um passo negativo(negative step) para inverter a lista:

python
numbers = [1, 2, 3, 4, 5]
 
# Inverter a lista
reversed_numbers = numbers[::-1]
print(reversed_numbers)  # Output: [5, 4, 3, 2, 1]

A fatia [::-1] significa "começar no fim, ir até o começo, andando para trás de 1 em 1". Isso é um idiomatismo comum em Python para inverter sequências.

14.2.5) Fatias Nunca Causam IndexError

Diferente de acessar um único elemento, o fatiamento é bem tolerante. Se você especificar índices fora do intervalo da lista, o Python simplesmente ajusta eles para caber:

python
numbers = [10, 20, 30, 40, 50]
 
# Pedindo mais do que existe
extended_slice = numbers[2:100]
print(extended_slice)  # Output: [30, 40, 50]
 
# Começando além do fim
empty_slice = numbers[10:20]
print(empty_slice)  # Output: []

Esse comportamento é útil porque significa que você não precisa se preocupar com limites exatos ao fatiar — o Python lida com casos de borda com elegância.

14.3) Modificando Listas e Métodos Comuns de Listas

14.3.1) Listas São Mutáveis: Alterando Elementos

Diferente de strings, que são imutáveis, listas são mutáveis — você pode mudar o conteúdo delas depois de criadas. Você pode modificar elementos individuais atribuindo novos valores a índices específicos:

python
# Comece com uma lista de preços
prices = [19.99, 24.50, 15.75, 32.00]
print(prices)  # Output: [19.99, 24.5, 15.75, 32.0]
 
# Atualize o segundo preço (índice 1)
prices[1] = 22.99
print(prices)  # Output: [19.99, 22.99, 15.75, 32.0]
 
# Atualize o último preço usando indexação negativa
prices[-1] = 29.99
print(prices)  # Output: [19.99, 22.99, 15.75, 29.99]

Essa mutabilidade é poderosa — significa que você pode atualizar dados no lugar sem criar novas listas. Porém, isso também significa que você precisa ter cuidado com mudanças não intencionais, o que vamos discutir na seção 14.6.

14.3.2) Adicionando Elementos com append()

O método append() adiciona um único item ao final de uma lista. Essa é uma das operações de lista mais usadas:

python
# Comece com um carrinho de compras vazio
cart = []
print(cart)  # Output: []
 
# Adicione itens um a um
cart.append("Milk")
print(cart)  # Output: ['Milk']
 
cart.append("Bread")
print(cart)  # Output: ['Milk', 'Bread']
 
cart.append("Eggs")
print(cart)  # Output: ['Milk', 'Bread', 'Eggs']

Repare que append() modifica a lista no lugar(in place) — ele não retorna uma nova lista. O método retorna None, então você não precisa atribuir o resultado dele:

python
scores = [85, 92, 78]
result = scores.append(95)
 
print(scores)   # Output: [85, 92, 78, 95]
print(result)   # Output: None

14.3.3) Inserindo Elementos em Posições Específicas com insert()

Enquanto append() sempre adiciona ao final, insert() permite que você adicione um item em qualquer posição. A sintaxe é list.insert(index, item):

python
students = ["Alice", "Charlie", "Diana"]
print(students)  # Output: ['Alice', 'Charlie', 'Diana']
 
# Insira "Bob" no índice 1 (entre Alice e Charlie)
students.insert(1, "Bob")
print(students)  # Output: ['Alice', 'Bob', 'Charlie', 'Diana']

Quando você insere em um índice, o item que estava naquela posição (e todos os itens depois dele) se deslocam para a direita:

python
numbers = [10, 20, 30, 40]
print(numbers)  # Output: [10, 20, 30, 40]
 
# Insira 25 no índice 2
numbers.insert(2, 25)
print(numbers)  # Output: [10, 20, 25, 30, 40]

Você pode inserir no começo usando o índice 0:

python
priorities = ["Medium", "Low"]
priorities.insert(0, "High")
print(priorities)  # Output: ['High', 'Medium', 'Low']

Se você especificar um índice além do tamanho da lista, insert() simplesmente adiciona o item no final (como append()):

python
items = [1, 2, 3]
items.insert(100, 4)
print(items)  # Output: [1, 2, 3, 4]

14.3.4) Removendo Elementos com remove()

O método remove() remove a primeira ocorrência de um valor específico da lista:

python
fruits = ["apple", "banana", "cherry", "banana", "date"]
print(fruits)  # Output: ['apple', 'banana', 'cherry', 'banana', 'date']
 
# Remova a primeira "banana"
fruits.remove("banana")
print(fruits)  # Output: ['apple', 'cherry', 'banana', 'date']

Repare que só a primeira "banana" foi removida — a segunda continua. Se você tentar remover um valor que não existe, o Python levanta um ValueError:

python
numbers = [10, 20, 30]
# AVISO: Tentando remover um valor que não existe - apenas para demonstração
# PROBLEMA: 40 não está na lista
# numbers.remove(40)  # ValueError: list.remove(x): x not in list

Para evitar esse erro, você pode checar se o item existe antes de removê-lo:

python
cart = ["Milk", "Bread", "Eggs"]
item_to_remove = "Butter"
 
if item_to_remove in cart:
    cart.remove(item_to_remove)
    print(f"Removed {item_to_remove}")
else:
    print(f"{item_to_remove} not in cart")
# Output: Butter not in cart

14.3.5) Removendo e Retornando Elementos com pop()

O método pop() remove um item em um índice específico e o retorna. Se você não especificar um índice, ele remove e retorna o último item:

python
scores = [85, 92, 78, 95, 88]
 
# Remova e pegue a última nota
last_score = scores.pop()
print(f"Removed: {last_score}")  # Output: Removed: 88
print(scores)  # Output: [85, 92, 78, 95]
 
# Remova e pegue a nota no índice 1
second_score = scores.pop(1)
print(f"Removed: {second_score}")  # Output: Removed: 92
print(scores)  # Output: [85, 78, 95]

Isso é útil quando você precisa processar itens de uma lista um de cada vez:

python
tasks = ["Write code", "Test code", "Deploy code"]
 
while len(tasks) > 0:
    current_task = tasks.pop(0)  # Remover do começo
    print(f"Working on: {current_task}")
 
# Output:
# Working on: Write code
# Working on: Test code
# Working on: Deploy code
 
print(tasks)  # Output: []

14.3.6) Estendendo Listas com extend()

O método extend() adiciona todos os itens de outra lista (ou qualquer iterável) ao final da lista atual:

python
primary_colors = ["red", "blue", "yellow"]
secondary_colors = ["green", "orange", "purple"]
 
# Adicione todas as cores secundárias às cores primárias
primary_colors.extend(secondary_colors)
print(primary_colors)
# Output: ['red', 'blue', 'yellow', 'green', 'orange', 'purple']

Isso é diferente de append(), que adicionaria a lista inteira como um único elemento:

python
colors1 = ["red", "blue"]
colors2 = ["green", "orange"]
 
# Usando append (adiciona a lista como um elemento só)
colors1.append(colors2)
print(colors1)  # Output: ['red', 'blue', ['green', 'orange']]
 
# Usando extend (adiciona cada elemento individualmente)
colors3 = ["red", "blue"]
colors3.extend(colors2)
print(colors3)  # Output: ['red', 'blue', 'green', 'orange']

14.3.7) Ordenando Listas com sort() e sorted()

O Python oferece duas formas de ordenar listas. O método sort() ordena a lista no lugar(in place) (modificando a original):

python
scores = [78, 95, 85, 92, 88]
scores.sort()
print(scores)  # Output: [78, 85, 88, 92, 95]

Para ordenar em ordem decrescente, use o parâmetro reverse:

python
scores = [78, 95, 85, 92, 88]
scores.sort(reverse=True)
print(scores)  # Output: [95, 92, 88, 85, 78]

A função sorted() (que vamos explorar mais no Capítulo 38) cria uma nova lista ordenada sem modificar a original:

python
original = [78, 95, 85, 92, 88]
sorted_scores = sorted(original)
 
print(original)       # Output: [78, 95, 85, 92, 88]
print(sorted_scores)  # Output: [78, 85, 88, 92, 95]

A ordenação funciona com strings também, usando ordem alfabética:

python
names = ["Charlie", "Alice", "Diana", "Bob"]
names.sort()
print(names)  # Output: ['Alice', 'Bob', 'Charlie', 'Diana']

14.3.8) Invertendo Listas com reverse()

O método reverse() inverte a lista no lugar(in place):

python
numbers = [1, 2, 3, 4, 5]
numbers.reverse()
print(numbers)  # Output: [5, 4, 3, 2, 1]

Isso é diferente de ordenar em ordem reversa — reverse() simplesmente inverte a ordem atual, seja ela qual for:

python
mixed = [3, 1, 4, 1, 5]
mixed.reverse()
print(mixed)  # Output: [5, 1, 4, 1, 3]

Lembre que você também pode inverter uma lista usando fatiamento: list[::-1]. A diferença é que o fatiamento cria uma nova lista, enquanto reverse() modifica a original.

14.3.9) Encontrando Elementos com index() e count()

O método index() retorna a posição da primeira ocorrência de um valor:

python
students = ["Alice", "Bob", "Charlie", "Diana", "Bob"]
 
# Encontrar onde "Charlie" está
position = students.index("Charlie")
print(f"Charlie is at index {position}")  # Output: Charlie is at index 2
 
# Encontrar o primeiro "Bob"
bob_position = students.index("Bob")
print(f"Bob is at index {bob_position}")  # Output: Bob is at index 1

Se o valor não existe, index() levanta um ValueError:

python
students = ["Alice", "Bob", "Charlie"]
# AVISO: Tentando encontrar um valor que não existe - apenas para demonstração
# PROBLEMA: 'Eve' não está na lista
# position = students.index("Eve")  # ValueError: 'Eve' is not in list

O método count() retorna quantas vezes um valor aparece:

python
numbers = [1, 2, 3, 2, 4, 2, 5]
twos = numbers.count(2)
print(f"The number 2 appears {twos} times")  # Output: The number 2 appears 3 times
 
# count pode retornar 0 se o item não existir
sixes = numbers.count(6)
print(f"The number 6 appears {sixes} times")  # Output: The number 6 appears 0 times

14.3.10) Limpando Todos os Elementos com clear()

O método clear() remove todos os itens de uma lista, deixando-a vazia:

python
cart = ["Milk", "Bread", "Eggs", "Butter"]
print(cart)  # Output: ['Milk', 'Bread', 'Eggs', 'Butter']
 
cart.clear()
print(cart)  # Output: []
print(len(cart))  # Output: 0

Isso é equivalente a atribuir uma lista vazia, mas clear() deixa mais explícita a intenção.

14.4) Excluindo Elementos de Lista com del

14.4.1) Usando del para Remover Elementos por Índice

A instrução del pode excluir elementos de uma lista em índices específicos:

python
students = ["Alice", "Bob", "Charlie", "Diana", "Eve"]
print(students)  # Output: ['Alice', 'Bob', 'Charlie', 'Diana', 'Eve']
 
# Excluir o elemento no índice 2
del students[2]
print(students)  # Output: ['Alice', 'Bob', 'Diana', 'Eve']

Diferente de pop(), del não retorna o valor removido — ele só exclui. Isso é útil quando você quer remover um item, mas não precisa usá-lo:

python
scores = [85, 92, 78, 95, 88]
 
# Remover a menor nota (no índice 2)
del scores[2]
print(scores)  # Output: [85, 92, 95, 88]

Você também pode usar índices negativos com del:

python
tasks = ["Task 1", "Task 2", "Task 3", "Task 4"]
 
# Excluir a última tarefa
del tasks[-1]
print(tasks)  # Output: ['Task 1', 'Task 2', 'Task 3']

14.4.2) Excluindo Fatias com del

A instrução del pode remover fatias inteiras de uma só vez:

python
numbers = [10, 20, 30, 40, 50, 60, 70]
print(numbers)  # Output: [10, 20, 30, 40, 50, 60, 70]
 
# Excluir elementos do índice 2 ao 4 (índices 2, 3, 4)
del numbers[2:5]
print(numbers)  # Output: [10, 20, 60, 70]

Isso é especialmente útil para remover intervalos de elementos:

python
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 
# Remover os três primeiros elementos
del data[:3]
print(data)  # Output: [4, 5, 6, 7, 8, 9, 10]
 
# Remover os dois últimos elementos
del data[-2:]
print(data)  # Output: [4, 5, 6, 7, 8]

Você pode até excluir um elemento sim e outro não usando fatiamento com step:

python
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
 
# Excluir a cada segundo elemento
del numbers[::2]
print(numbers)  # Output: [1, 3, 5, 7, 9]

14.4.3) Comparando del, remove() e pop()

Vamos deixar claro quando usar cada método de exclusão:

python
# Lista de exemplo para comparação
items = ["apple", "banana", "cherry", "date", "elderberry"]
 
# Use remove() quando você sabe o VALOR a excluir
items_copy1 = items.copy()
items_copy1.remove("cherry")  # Remove o primeiro "cherry"
print(items_copy1)  # Output: ['apple', 'banana', 'date', 'elderberry']
 
# Use pop() quando você sabe o ÍNDICE e precisa do valor
items_copy2 = items.copy()
removed_item = items_copy2.pop(2)  # Remove e retorna o item no índice 2
print(f"Removed: {removed_item}")  # Output: Removed: cherry
print(items_copy2)  # Output: ['apple', 'banana', 'date', 'elderberry']
 
# Use del quando você sabe o ÍNDICE mas não precisa do valor
items_copy3 = items.copy()
del items_copy3[2]  # Só remove o item no índice 2
print(items_copy3)  # Output: ['apple', 'banana', 'date', 'elderberry']

14.5) Iterando Sobre Listas com Loops for

14.5.1) Iteração Básica de Lista

Uma das operações mais comuns com listas é processar cada item em sequência. O loop for (que aprendemos no Capítulo 12) é perfeito para isso:

python
students = ["Alice", "Bob", "Charlie", "Diana"]
 
# Processar cada aluno
for student in students:
    print(f"Hello, {student}!")
 
# Output:
# Hello, Alice!
# Hello, Bob!
# Hello, Charlie!
# Hello, Diana!

A variável do loop (student neste caso) assume cada valor da lista, um por vez, em ordem. Você pode dar para essa variável qualquer nome que faça sentido:

python
scores = [85, 92, 78, 95, 88]
 
# Calcular e exibir a nota (grade) de cada score
for score in scores:
    if score >= 90:
        grade = "A"
    elif score >= 80:
        grade = "B"
    else:
        grade = "C"
    print(f"Score {score} is a {grade}")
 
# Output:
# Score 85 is a B
# Score 92 is a A
# Score 78 is a C
# Score 95 is a A
# Score 88 is a B

14.5.2) Processando Itens Correspondentes de Múltiplas Listas

Às vezes você precisa trabalhar com dados relacionados armazenados em listas separadas. Vamos aprender sobre a função zip() em detalhes no Capítulo 38, mas aqui vai uma prévia rápida de como ela pode ajudar a processar itens correspondentes:

python
# Vamos aprender sobre zip() no Capítulo 38, mas por enquanto, aqui vai um exemplo simples
students = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]
 
# Processar pares correspondentes
for student, score in zip(students, scores):
    print(f"{student} scored {score}")
 
# Output:
# Alice scored 85
# Bob scored 92
# Charlie scored 78

A função zip() emparelha elementos de múltiplas listas, o que é útil quando você tem dados relacionados em listas separadas. Vamos explorar isso e outras ferramentas de iteração em profundidade no Capítulo 38.

14.6) Copiando Listas e Evitando Referências Compartilhadas

14.6.1) Entendendo Referências de Lista

Quando você atribui uma lista a uma variável, o Python não cria uma cópia da lista — ele cria uma referência(reference) para o mesmo objeto de lista na memória. Isso significa que múltiplas variáveis podem se referir à mesma lista:

python
original = [1, 2, 3]
reference = original  # As duas variáveis apontam para a MESMA lista
 
# Modificar por uma variável afeta a outra
reference.append(4)
print(original)   # Output: [1, 2, 3, 4]
print(reference)  # Output: [1, 2, 3, 4]

Esse comportamento pode ser surpreendente se você espera que reference seja uma cópia independente. Vamos ver por que isso importa:

python
# Cenário: você quer acompanhar mudanças em um carrinho de compras
cart = ["Milk", "Bread"]
backup = cart  # Tentando salvar o estado original
 
# Adicionar mais itens
cart.append("Eggs")
cart.append("Butter")
 
# Verificar o "backup"
print(backup)  # Output: ['Milk', 'Bread', 'Eggs', 'Butter']

O backup também mudou! Isso acontece porque backup e cart são dois nomes para o mesmo objeto de lista.

Variável: cart

Objeto de lista: ⦗'Milk', 'Bread', 'Eggs', 'Butter'⦘

Variável: backup

14.6.2) Criando Cópias Independentes com Fatiamento

Para criar uma cópia realmente independente, use fatiamento com [:]:

python
original = [1, 2, 3]
copy = original[:]  # Cria uma NOVA lista com o mesmo conteúdo
 
# Modificar a cópia não afeta a original
copy.append(4)
print(original)  # Output: [1, 2, 3]
print(copy)      # Output: [1, 2, 3, 4]

Agora vamos corrigir nosso exemplo do carrinho de compras:

python
cart = ["Milk", "Bread"]
backup = cart[:]  # Criar uma cópia independente
 
# Adicionar mais itens ao carrinho
cart.append("Eggs")
cart.append("Butter")
 
# O backup continua sem alterações
print(cart)    # Output: ['Milk', 'Bread', 'Eggs', 'Butter']
print(backup)  # Output: ['Milk', 'Bread']

14.6.3) Criando Cópias com o Método copy()

Listas também têm um método copy() que faz a mesma coisa que [:]:

python
original = [10, 20, 30]
copy = original.copy()
 
copy.append(40)
print(original)  # Output: [10, 20, 30]
print(copy)      # Output: [10, 20, 30, 40]

Tanto [:] quanto copy() criam cópias rasas(shallow copies), o que vamos discutir a seguir.

14.6.4) A Limitação da Cópia Rasa

Tanto [:] quanto copy() criam cópias rasas(shallow copies). Isso significa que elas copiam a estrutura da lista, mas se a lista contiver outros objetos mutáveis (como outras listas), esses objetos internos ainda são compartilhados:

python
# Uma lista contendo listas
original = [[1, 2], [3, 4], [5, 6]]
copy = original[:]
 
# Modificar a estrutura da lista externa é independente
copy.append([7, 8])
print(original)  # Output: [[1, 2], [3, 4], [5, 6]]
print(copy)      # Output: [[1, 2], [3, 4], [5, 6], [7, 8]]
 
# Mas modificar uma lista interna afeta as duas!
copy[0].append(99)
print(original)  # Output: [[1, 2, 99], [3, 4], [5, 6]]
print(copy)      # Output: [[1, 2, 99], [3, 4], [5, 6], [7, 8]]

Por que isso acontece? Porque a cópia rasa cria uma nova lista externa, mas as listas internas ainda são referências compartilhadas:

lista externa original

Lista interna: ⦗1, 2, 99⦘

Lista interna: ⦗3, 4⦘

Lista interna: ⦗5, 6⦘

lista externa copy

Lista interna: ⦗7, 8⦘

Para estruturas aninhadas, você precisaria de uma cópia profunda(deep copy), que vamos aprender quando explorarmos o módulo copy em capítulos posteriores. Por enquanto, saiba que cópias rasas funcionam perfeitamente para listas de itens imutáveis (números, strings, tuplas), mas exigem cuidado com estruturas mutáveis aninhadas.

14.6.5) Quando Referências Compartilhadas São Úteis

Às vezes você quer que múltiplas variáveis se refiram à mesma lista. Isso é útil quando você precisa modificar uma lista de diferentes partes do seu código:

python
# Uma função que modifica uma lista no lugar
def add_bonus_points(scores, bonus):
    for i in range(len(scores)):
        scores[i] = scores[i] + bonus
 
# A lista original é modificada
student_scores = [85, 92, 78]
add_bonus_points(student_scores, 5)
print(student_scores)  # Output: [90, 97, 83]

Isso funciona porque a função recebe uma referência para a lista original, não uma cópia. Vamos explorar isso mais quando estudarmos funções em detalhes na Parte V.

14.7) Usando enumerate() ao Percorrer Listas

14.7.1) A Necessidade de Ter Índice e Valor

Às vezes, ao iterar sobre uma lista, você precisa tanto do índice quanto do valor. Uma abordagem é usar range(len(list)):

python
students = ["Alice", "Bob", "Charlie", "Diana"]
 
for i in range(len(students)):
    print(f"Student {i}: {students[i]}")
 
# Output:
# Student 0: Alice
# Student 1: Bob
# Student 2: Charlie
# Student 3: Diana

Isso funciona, mas não é muito elegante. Você tem que usar students[i] para acessar cada valor, o que é menos legível do que iterar diretamente sobre os valores.

14.7.2) Usando enumerate() para um Código Mais Limpo

A função enumerate() fornece uma solução melhor. Ela retorna tanto o índice quanto o valor para cada item:

python
students = ["Alice", "Bob", "Charlie", "Diana"]
 
for index, student in enumerate(students):
    print(f"Student {index}: {student}")
 
# Output:
# Student 0: Alice
# Student 1: Bob
# Student 2: Charlie
# Student 3: Diana

A sintaxe for index, value in enumerate(list) desempacota cada par que enumerate() produz. Isso é bem mais legível do que usar range(len()).

14.7.3) Iniciando enumerate() em um Número Diferente

Por padrão, enumerate() começa a contar em 0. Você pode especificar um número inicial diferente com o parâmetro start:

python
students = ["Alice", "Bob", "Charlie", "Diana"]
 
# Começar a contagem em 1 em vez de 0
for position, student in enumerate(students, start=1):
    print(f"Position {position}: {student}")
 
# Output:
# Position 1: Alice
# Position 2: Bob
# Position 3: Charlie
# Position 4: Diana

Isso é útil quando você quer mostrar uma numeração amigável para humanos (começando em 1) em vez de uma indexação amigável para programadores (começando em 0).

Exemplos Práticos com enumerate()

Aqui vai um exemplo prático exibindo um menu numerado:

python
menu_items = ["New Game", "Load Game", "Settings", "Quit"]
 
print("Main Menu:")
for number, item in enumerate(menu_items, start=1):
    print(f"{number}. {item}")
 
# Output:
# Main Menu:
# 1. New Game
# 2. Load Game
# 3. Settings
# 4. Quit

14.7.4) Modificando Listas com enumerate()

Você pode usar enumerate() quando precisa modificar elementos da lista com base na posição deles:

python
# Adicionar bônus baseado na posição às notas
scores = [85, 92, 78, 95, 88]
 
for index, score in enumerate(scores):
    # O primeiro aluno recebe 5 pontos de bônus, o segundo recebe 4, etc.
    bonus = 5 - index
    if bonus > 0:
        scores[index] = score + bonus
 
print(scores)  # Output: [90, 96, 81, 97, 89]

14.8) Padrões Comuns com Listas: Buscar, Filtrar e Agregar Dados

14.8.1) Buscando Itens em Listas

Uma das tarefas mais comuns é verificar se uma lista contém um item específico. O operador in (que aprendemos no Capítulo 7) deixa isso simples:

python
students = ["Alice", "Bob", "Charlie", "Diana"]
 
# Verificar se um aluno está na lista
if "Charlie" in students:
    print("Charlie is enrolled")  # Output: Charlie is enrolled
 
if "Eve" not in students:
    print("Eve is not enrolled")  # Output: Eve is not enrolled

Para encontrar a posição de um item, use o método index() (coberto na seção 14.3.9), mas lembre de checar se o item existe primeiro:

python
scores = [85, 92, 78, 95, 88]
target_score = 95
 
if target_score in scores:
    position = scores.index(target_score)
    print(f"Score {target_score} found at index {position}")
    # Output: Score 95 found at index 3
else:
    print(f"Score {target_score} not found")

14.8.2) Encontrando os Valores Máximo e Mínimo

As funções embutidas max() e min() do Python funcionam com listas:

python
scores = [85, 92, 78, 95, 88, 91, 76]
 
highest_score = max(scores)
lowest_score = min(scores)
 
print(f"Highest score: {highest_score}")  # Output: Highest score: 95
print(f"Lowest score: {lowest_score}")    # Output: Lowest score: 76

14.8.3) Calculando Agregados: Soma, Média e Contagem

Computar totais e médias é uma operação fundamental com listas:

python
scores = [85, 92, 78, 95, 88, 91, 76, 89]
 
# Calcular total e média
total = sum(scores)
count = len(scores)
average = total / count
 
print(f"Total: {total}")        # Output: Total: 694
print(f"Count: {count}")        # Output: Count: 8
print(f"Average: {average:.2f}")  # Output: Average: 86.75

Aqui vai um exemplo prático calculando o total de um carrinho de compras:

python
cart_items = ["Milk", "Bread", "Eggs", "Butter", "Cheese"]
prices = [3.99, 2.49, 4.99, 5.49, 6.99]
 
# Calcular o custo total
total_cost = sum(prices)
item_count = len(cart_items)
 
print(f"Items in cart: {item_count}")
print(f"Total cost: ${total_cost:.2f}")
 
# Output:
# Items in cart: 5
# Total cost: $23.95

14.9) Mutabilidade de Listas e Truthiness em Condições

14.9.1) Entendendo a Mutabilidade de Listas na Prática

Vimos ao longo deste capítulo que listas são mutáveis — elas podem ser alteradas após a criação. Essa mutabilidade é o que torna listas tão poderosas para armazenar e manipular coleções de dados. Vamos consolidar nosso entendimento com um exemplo abrangente:

python
# Comece com uma lista de tarefas vazia
tasks = []
print(f"Initial tasks: {tasks}")  # Output: Initial tasks: []
 
# Adicionar tarefas
tasks.append("Write code")
tasks.append("Test code")
tasks.append("Deploy code")
print(f"After adding: {tasks}")
# Output: After adding: ['Write code', 'Test code', 'Deploy code']
 
# Inserir uma tarefa urgente no começo
tasks.insert(0, "Review requirements")
print(f"After inserting: {tasks}")
# Output: After inserting: ['Review requirements', 'Write code', 'Test code', 'Deploy code']
 
# Concluir e remover a primeira tarefa
completed = tasks.pop(0)
print(f"Completed: {completed}")  # Output: Completed: Review requirements
print(f"Remaining: {tasks}")
# Output: Remaining: ['Write code', 'Test code', 'Deploy code']
 
# Modificar uma tarefa
tasks[1] = "Test code thoroughly"
print(f"After modifying: {tasks}")
# Output: After modifying: ['Write code', 'Test code thoroughly', 'Deploy code']

14.9.2) Mutabilidade vs Imutabilidade: Listas vs Strings

É importante entender a diferença entre listas mutáveis e strings imutáveis. Com strings, as operações criam novas strings em vez de modificar a original:

python
# Strings são imutáveis
text = "hello"
text.upper()  # Cria uma nova string, não muda a original
print(text)  # Output: hello (unchanged)
 
# Para "mudar" uma string, você precisa reatribuir
text = text.upper()
print(text)  # Output: HELLO
 
# Listas são mutáveis
numbers = [1, 2, 3]
numbers.append(4)  # Modifica a lista no lugar
print(numbers)  # Output: [1, 2, 3, 4] (changed)

Essa diferença afeta como você trabalha com esses tipos:

python
# Operações com string exigem reatribuição
name = "alice"
name = name.capitalize()  # Precisa reatribuir para ver a mudança
print(name)  # Output: Alice
 
# Operações com lista modificam no lugar
scores = [85, 92, 78]
scores.append(95)  # Não precisa reatribuir
print(scores)  # Output: [85, 92, 78, 95]

14.9.3) Usando Listas em Contextos Booleanos

Listas têm truthiness: uma lista vazia é considerada False, e qualquer lista não vazia é considerada True. Isso é útil em instruções condicionais:

python
# Lista vazia é falsy
empty_cart = []
if empty_cart:
    print("Cart has items")
else:
    print("Cart is empty")  # Output: Cart is empty
 
# Lista não vazia é truthy
cart_with_items = ["Milk", "Bread"]
if cart_with_items:
    print("Cart has items")  # Output: Cart has items

Esse padrão é bastante usado para checar se uma lista tem elementos antes de processar:

python
students = ["Alice", "Bob", "Charlie"]
 
if students:
    print(f"We have {len(students)} students")
    for student in students:
        print(f"  - {student}")
else:
    print("No students enrolled")
 
# Output:
# We have 3 students
#   - Alice
#   - Bob
#   - Charlie

14.9.4) Padrão Prático: Processar Até Esvaziar

A truthiness das listas permite um padrão útil para processar itens até que uma lista esteja vazia:

python
# Processar tarefas até não sobrar nenhuma
tasks = ["Task 1", "Task 2", "Task 3"]
 
while tasks:  # Continuar enquanto a lista não estiver vazia
    current_task = tasks.pop(0)
    print(f"Processing: {current_task}")
 
print("All tasks completed!")
 
# Output:
# Processing: Task 1
# Processing: Task 2
# Processing: Task 3
# All tasks completed!

14.9.5) Verificando Listas Vazias: Explícito vs Implícito

Existem duas formas de verificar se uma lista está vazia:

python
items = []
 
# Verificação implícita (Pythonic)
if not items:
    print("List is empty")  # Output: List is empty
 
# Verificação explícita (também válida)
if len(items) == 0:
    print("List is empty")  # Output: List is empty

A verificação implícita (if not items:) geralmente é preferida em Python porque é mais concisa e funciona com qualquer tipo de coleção. Porém, as duas abordagens estão corretas e você vai ver ambas em código real.

14.9.6) Mutabilidade e Comportamento de Funções

Quando você passa uma lista para uma função (o que vamos explorar em detalhes na Parte V), a função recebe uma referência para o mesmo objeto de lista. Isso significa que a função pode modificar a lista original:

python
def add_item(shopping_list, item):
    shopping_list.append(item)
    print(f"Added {item}")
 
# A lista original é modificada
cart = ["Milk", "Bread"]
print(f"Before: {cart}")  # Output: Before: ['Milk', 'Bread']
 
add_item(cart, "Eggs")     # Output: Added Eggs
print(f"After: {cart}")    # Output: After: ['Milk', 'Bread', 'Eggs']

Esse comportamento é diferente de tipos imutáveis como strings e números, em que o valor original não pode ser alterado por uma função. Entender essa distinção é crucial para escrever programas corretos.


Listas são uma das estruturas de dados mais fundamentais e versáteis do Python. Elas fornecem uma coleção ordenada e mutável que pode crescer e encolher conforme necessário, tornando-as perfeitas para armazenar e processar sequências de dados relacionados. Você aprendeu como criar listas, acessar seus elementos por indexação e fatiamento, modificá-las com vários métodos, iterar sobre elas de forma eficiente e entender sua natureza mutável.

Os padrões que exploramos — busca, filtragem, agregação e transformação de dados — formam a base para trabalhar com coleções em Python. Conforme você continuar aprendendo, vai descobrir formas ainda mais poderosas de trabalhar com listas, incluindo list comprehensions (Capítulo 35) e técnicas avançadas de iteração (Capítulos 36-37). Mas os fundamentos que você dominou neste capítulo vão te servir muito bem ao longo da sua jornada de programação em Python.


© 2025. Primesoft Co., Ltd.
support@primesoft.ai