O laço for é uma das estruturas de controle mais fundamentais e versáteis em Python. Ao contrário de muitas linguagens que usam índices e condições para iterar, o for em Python é um for-each: ele percorre diretamente os elementos de um objeto iterável, como listas, strings, tuplas, dicionários, etc. Isso torna o código mais limpo, legível e menos propenso a erros de indexação.

Nesta aula, vamos dominar o uso do for em quatro contextos essenciais: iteração simples sobre sequências, a função range() para gerar sequências numéricas, enumerate() para obter índice e valor simultaneamente, e zip() para percorrer múltiplas sequências em paralelo. Ao final, você terá uma base sólida para escrever loops eficientes e expressivos.

Iteração sobre sequências

O laço for em Python é projetado para iterar sobre qualquer objeto iterável. Uma sequência é um tipo de iterável que suporta acesso por índice (como listas, tuplas, strings) e mantém uma ordem. Quando usamos for com uma sequência, em cada iteração a variável de controle recebe o próximo elemento da sequência, sem necessidade de gerenciar índices manualmente.

Vejamos exemplos com diferentes tipos de sequências:

# Iterando sobre uma lista
frutas = ["maçã", "banana", "cereja"]
for fruta in frutas:
    print(fruta)

# Iterando sobre uma string (caractere por caractere)
palavra = "Python"
for letra in palavra:
    print(letra)

# Iterando sobre uma tupla
coordenadas = (10, 20)
for coord in coordenadas:
    print(coord)

É importante notar que o laço for também funciona com outros iteráveis, como dicionários (itera sobre as chaves por padrão), conjuntos (sem ordem garantida) e até objetos de arquivo (linha por linha). Porém, o foco aqui são as sequências, que são a base para entender as funções a seguir.

Quando você precisa modificar os elementos de uma lista enquanto itera, é recomendável criar uma nova lista ou usar índices via range(), pois alterar a lista durante a iteração pode causar comportamentos inesperados. Por exemplo:

# Multiplicar cada elemento por 2 - forma correta com nova lista
numeros = [1, 2, 3, 4]
dobrados = []
for n in numeros:
    dobrados.append(n * 2)
print(dobrados)  # [2, 4, 6, 8]

# Tentativa de modificar durante iteração - não funciona como esperado
for n in numeros:
    n = n * 2  # isso não altera a lista original
print(numeros)  # [1, 2, 3, 4]

range

A função range() é usada para gerar uma sequência de números inteiros. Ela é frequentemente combinada com o laço for para repetir um bloco de código um número específico de vezes ou para iterar sobre índices. range() retorna um objeto range, que é eficiente em memória, pois não armazena todos os valores de uma vez — ela gera os números sob demanda.

A sintaxe básica é range(start, stop, step), onde:

  • start (opcional, padrão 0): o primeiro número da sequência.
  • stop (obrigatório): o limite superior exclusivo (o número não é incluído).
  • step (opcional, padrão 1): o incremento entre cada número.

Exemplos práticos:

# range(stop) - gera 0 até stop-1
for i in range(5):
    print(i)  # imprime 0,1,2,3,4

# range(start, stop)
for i in range(2, 6):
    print(i)  # imprime 2,3,4,5

# range(start, stop, step)
for i in range(0, 10, 2):
    print(i)  # imprime 0,2,4,6,8

# Usando range para iterar sobre índices de uma lista
frutas = ["maçã", "banana", "cereja"]
for i in range(len(frutas)):
    print(f"Índice {i}: {frutas[i]}")

Uma observação importante: range() aceita números negativos para step, o que permite contar para trás. Por exemplo, range(10, 0, -2) gera 10, 8, 6, 4, 2. Além disso, em Python 3, range() é um objeto preguiçoso, mas pode ser convertido em lista com list(range(...)) se necessário.

O uso de range(len(lista)) é comum quando precisamos do índice para modificar a lista ou acessar outra sequência de mesmo tamanho. No entanto, para acessar índice e valor simultaneamente, a função enumerate() é mais elegante, como veremos a seguir.

enumerate

Muitas vezes, ao percorrer uma sequência, precisamos saber a posição (índice) de cada elemento. Embora seja possível usar range(len(seq)), a função enumerate() fornece uma maneira mais limpa e Pythonica de obter tanto o índice quanto o valor em cada iteração.

enumerate(iterável, start=0) retorna um objeto que produz tuplas contendo um contador (índice) e o elemento correspondente. O parâmetro start permite definir o valor inicial do contador (padrão é 0).

Exemplo básico:

frutas = ["maçã", "banana", "cereja"]
for i, fruta in enumerate(frutas):
    print(f"{i}: {fruta}")
# Saída:
# 0: maçã
# 1: banana
# 2: cereja

Se quisermos que a contagem comece em 1, usamos start=1:

for i, fruta in enumerate(frutas, start=1):
    print(f"{i}. {fruta}")
# Saída:
# 1. maçã
# 2. banana
# 3. cereja

Essa função é extremamente útil em diversas situações, como ao exibir listas numeradas, ao acompanhar o progresso de um loop, ou quando precisamos do índice para modificar o elemento. Por exemplo, para transformar uma lista de palavras em maiúsculas apenas em posições pares:

palavras = ["olá", "mundo", "python", "for"]
for i, palavra in enumerate(palavras):
    if i % 2 == 0:
        palavras[i] = palavra.upper()
print(palavras)  # ['OLÁ', 'mundo', 'PYTHON', 'for']

Note que enumerate() funciona com qualquer iterável, não apenas sequências, mas o uso com sequências é o mais comum e didático.

zip

A função zip() é utilizada para combinar múltiplos iteráveis em um único iterável de tuplas, onde o primeiro elemento de cada iterável forma a primeira tupla, o segundo elemento forma a segunda, e assim por diante. O iterável resultante tem o comprimento do menor iterável fornecido (ou seja, ele para quando o menor se esgota).

Exemplo com duas listas:

nomes = ["Ana", "Bruno", "Carla"]
idades = [25, 30, 22]
for nome, idade in zip(nomes, idades):
    print(f"{nome} tem {idade} anos")
# Saída:
# Ana tem 25 anos
# Bruno tem 30 anos
# Carla tem 22 anos

Também é possível usar zip() com mais de duas sequências e até mesmo com iteráveis de tamanhos diferentes, mas o resultado será truncado no menor:

letras = ["a", "b", "c"]
numeros = [1, 2, 3, 4]
for letra, numero in zip(letras, numeros):
    print(letra, numero)
# Saída: a 1, b 2, c 3 (o 4 é ignorado)

Um padrão comum é usar zip() para percorrer duas listas de mesmo tamanho simultaneamente, evitando o uso de índices. Isso torna o código mais legível e seguro. Por exemplo, para somar elementos correspondentes de duas listas:

a = [1, 2, 3]
b = [4, 5, 6]
somas = []
for x, y in zip(a, b):
    somas.append(x + y)
print(somas)  # [5, 7, 9]

Outra aplicação comum é a criação de dicionários a partir de duas listas: dict(zip(chaves, valores)).

Vale notar que, em Python 3, zip() retorna um iterador (objeto zip) que só pode ser percorrido uma vez. Se precisar de uma lista de tuplas, use list(zip(...)).

Boas Práticas

Ao usar o laço for em Python, siga estas recomendações para escrever código claro e eficiente:

  • Prefira iterar diretamente sobre os elementos em vez de usar índices, a menos que seja necessário modificar a sequência.
  • Use enumerate() quando precisar do índice e do valor, em vez de range(len(...)).
  • Use zip() para percorrer múltiplas sequências em paralelo, evitando o uso de índices.
  • Evite modificar a lista enquanto itera sobre ela; crie uma nova lista ou itere sobre uma cópia.
  • Use nomes de variáveis significativos no laço (ex.: for aluno in alunos em vez de for x in alunos).
  • Quando precisar de um loop apenas para repetir um bloco N vezes, use for _ in range(N), onde _ indica que a variável não é usada.

Exercícios

  1. Escreva um programa que use um laço for para imprimir cada caractere de uma string fornecida, separado por um hífen.

    ✓ Resposta:
    texto = "Python"
    for i, char in enumerate(texto):
        if i == len(texto) - 1:
            print(char)
        else:
            print(char, end="-")
    # Saída: P-y-t-h-o-n
    
  2. Dada uma lista de números, calcule a soma de todos os elementos usando um laço for e a função range() (sem usar a função sum()).

    ✓ Resposta:
    numeros = [10, 20, 30, 40]
    soma = 0
    for i in range(len(numeros)):
        soma += numeros[i]
    print(soma)  # 100
    
  3. Utilize a função enumerate() para percorrer a lista nomes = ["Ana", "Bruno", "Carla"] e imprimir cada nome com seu índice na forma 'Índice X: Nome'.

    ✓ Resposta:
    nomes = ["Ana", "Bruno", "Carla"]
    for i, nome in enumerate(nomes):
        print(f"Índice {i}: {nome}")
    # Saída:
    # Índice 0: Ana
    # Índice 1: Bruno
    # Índice 2: Carla
    
  4. Dadas duas listas de mesmo tamanho, produtos = ["camiseta", "calça", "tênis"] e precos = [49.90, 89.90, 199.90], use zip() para imprimir cada produto com seu preço.

    ✓ Resposta:
    produtos = ["camiseta", "calça", "tênis"]
    precos = [49.90, 89.90, 199.90]
    for produto, preco in zip(produtos, precos):
        print(f"{produto}: R${preco:.2f}")
    # Saída:
    # camiseta: R$49.90
    # calça: R$89.90
    # tênis: R$199.90
    
  5. Crie um programa que use range() para imprimir todos os números pares de 0 a 20 (inclusive) e, ao final, imprima quantos números pares foram exibidos.

    ✓ Resposta:
    contador = 0
    for num in range(0, 21, 2):
        print(num)
        contador += 1
    print(f"Total de pares: {contador}")
    # Saída: 0,2,4,...20 e Total de pares: 11
    

Referências