Estrutura de repetição#

Até agora o nosso código executa apenas uma vez, com a possibilidade de tomada de decisão através da estrutura condicional if-else-elif. No entanto, muitas vezes precisamos executar um bloco de código várias vezes. Para isso, utilizamos as estruturas de repetição.

Em Python, temos duas estruturas de repetição, também chamadas de laços: for e while. A estrutura for é utilizada quando sabemos o número de vezes que o bloco de código deve ser executado. Já a estrutura while é utilizada quando não sabemos o número de vezes que o bloco de código deve ser executado, mas sabemos a condição de parada.

Nesta seção, vamos aprender mais sobre estas duas estruturas.

Laço for#

O laço for é usado sempre que sabemos quantas vezes queremos executar um bloco de código. A sintaxe básica do laço for é a seguinte:

for variavel in sequencia:
    # bloco de código a ser repetido

onde sequencia pode ser uma lista, tupla, string, dicionário, set ou qualquer outra sequencia, e variavel é uma variável temporária que assume o valor de cada elemento da sequência a cada iteração.

Lembrete (strings e sequencia)

Lembre-se que uma string é uma sequencia de caracteres!

Reparem também que todo bloco de código que será repetido deve estar identado.

Ou seja, para usarmos o laço for, necessariamente eu preciso ter uma sequencia que será percorrida elemento a elemento, e esta sequencia tem tamanho conhecido. Por isso que o for é usado quando sabemos quantas vezes queremos executar um bloco de código.

Vamos ver um exemplo simples de uso do laço for:

for variavel in range(1, 10):
    print(variavel)
1
2
3
4
5
6
7
8
9

No exemplo acima, temos variavel que assume cada valor da sequencia.

A função range#

E para a sequencia, temos uma novidade: a função range(). A função range() é uma função que gera uma sequencia de números, e é muito utilizada em conjunto com o laço for. A função range() é bem simples pode receber até três argumentos: range(inicio, fim, passo). Basicamente ela gera uma sequencia de números que começa em inicio, vai até fim (sem incluir o fim) pulando os valores através do passo (1 em 1, 2 em 2, etc.). Se o passo não for informado, o valor padrão é 1. Fique a vontade para consultar a documentação da função range() aqui.

Se isolarmos o range(1, 10) do exemplo acima:

range(1, 10)
range(1, 10)

Vejam que ele não imprime algo muito útil. Vamos verificar o tipo do range(1, 10) e convertê-lo para uma lista?

print(type(range(1, 10)))
print(list(range(1, 10)))
<class 'range'>
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Apesar do range() ser um tipo de objeto até então desconhecido, ele também é uma sequencia e pode ser usado diretamente em laços for, sem necessariamente ser transformado em uma lista ou tupla, conforme vimos no exemplo. Quando transformado em uma lista, o range() nos mostra claramente a sequencia de números que ele gera, no caso, de 1 a 9 (o 10 não é incluso).

E, retomando o laço for, para cada iteração, variavel assume um valor da sequencia que é impressa na tela.

Um ponto importante sobre a variável temporária criada dentro do laço for é que ela não necessariamente precisa ser usada. Se quisermos, por exemplo, imprimir a mensagem «Olá, eu estou aprendendo Python!» 10 vezes, ainda sim precisamos criar a sequencia com 10 elementos para percorrê-la item a item, porém sem usar os elementos da sequencia. Vejam:

for variavel in range(10):
    print("Olá, eu estou aprendendo Python!")
Olá, eu estou aprendendo Python!
Olá, eu estou aprendendo Python!
Olá, eu estou aprendendo Python!
Olá, eu estou aprendendo Python!
Olá, eu estou aprendendo Python!
Olá, eu estou aprendendo Python!
Olá, eu estou aprendendo Python!
Olá, eu estou aprendendo Python!
Olá, eu estou aprendendo Python!
Olá, eu estou aprendendo Python!

Em tais casos nos quais os elementos da sequencia não são utilizados em todo o código do laço for, é comum utilizar o caractere _ para representar a variável no laço for indicando de forma clara que ela não será utilizada.

for _ in range(10):
    print("Olá, eu estou aprendendo Python!")
Olá, eu estou aprendendo Python!
Olá, eu estou aprendendo Python!
Olá, eu estou aprendendo Python!
Olá, eu estou aprendendo Python!
Olá, eu estou aprendendo Python!
Olá, eu estou aprendendo Python!
Olá, eu estou aprendendo Python!
Olá, eu estou aprendendo Python!
Olá, eu estou aprendendo Python!
Olá, eu estou aprendendo Python!

Um outro caso bastante comum é quando precisamos não apenas do valor do elemento da sequencia, mas também do índice do elemento. Vejamos no exemplo abaixo:

meses = [
    "Janeiro",
    "Fevereiro",
    "Março",
    "Abril",
    "Maio",
    "Junho",
    "Julho",
    "Agosto",
    "Setembro",
    "Outubro",
    "Novembro",
    "Dezembro",
]

for indice in range(len(meses)):
    numero_mes = indice + 1
    print(f"{meses[indice]} é o {numero_mes}° mês do ano.")
Janeiro é o 1° mês do ano.
Fevereiro é o 2° mês do ano.
Março é o 3° mês do ano.
Abril é o 4° mês do ano.
Maio é o 5° mês do ano.
Junho é o 6° mês do ano.
Julho é o 7° mês do ano.
Agosto é o 8° mês do ano.
Setembro é o 9° mês do ano.
Outubro é o 10° mês do ano.
Novembro é o 11° mês do ano.
Dezembro é o 12° mês do ano.

Por qual motivo não simplesmente usamos for mes in meses nesse caso? O laço for percorre cada elemento da lista, mas não acessa o índice daquele elemento. Portanto, precisamos fazer uma outra sequencia que contenha todos os índices da nossa sequencia original. Para isso, usamos a junção das função range(len(meses)) que trará uma sequencia de números de 0 a 11 (12 elementos). O laço for itera, portanto, através de uma sequencia que representam os índices da lista meses. Só que ainda precisamos, a partir do índice, acessar o valor do elemento da lista meses. Para isso, usamos a notação meses[indice]. E como a indexação começa por 0 em Python, ainda precisamos criar uma outra variável que representará o número do mês, que começa em 1 e vai até 12, diferente do indice que começa em 0 e vai até 11.

Desempacotamento#

Antes de seguirmos com o laço for, vamos introduzir um conceito super importante que será usado: desempacotamento. O desempacotamento é uma técnica que permite quebrar uma sequencia em partes menores.

De forma bem simples, é como se tivéssemos uma caixa com vários itens e quiséssemos separar cada item em uma variável diferente. Vejamos o exemplo abaixo:

lista = [1, 2, 3]

# Podemos capturar cada valor da lista através da técnica de desempacotamento
a, b, c = lista

print(a, b, c)
1 2 3

O desempacotamento funciona como se quebrássemos os elementos de uma dada sequencia, no exemplo acima uma lista, e atribuíssemos cada elemento a uma variável diferente. Em palavras mais simples, podemos «desembrulhar» os elementos da sequencia em variáveis.

E porque isso está sendo explicando somente nesse capítulo de laços de repetição? Vamos ver um exemplo prático:

vendas = [("Camiseta", 4, 25.00), ("Calça Jeans", 2, 100.00), ("Tênis", 1, 150.00)]

Supondo que temos a estrutura acima, vendas, onde cada elemento da lista é uma tupla, e cada tupla contém 3 elementos: o nome do produto, a quantidade vendida e o preço unitário. E gostaríamos de imprimir na tela o nome de cada produto, sua respectiva quantidade vendida e valor total. Como faríamos isso? Talvez você pense em fazer da seguinte forma:

vendas = [("Camiseta", 4, 25.00), ("Calça Jeans", 2, 100.00), ("Tênis", 1, 150.00)]

for item in vendas:
    produto = item[0]
    quantidade = item[1]
    preco_unitario = item[2]

    valor_total = quantidade * preco_unitario
    print(
        f"Produto: {produto}, quantidade: {quantidade}, valor total: R${valor_total:.2f}"
    )
Produto: Camiseta, quantidade: 4, valor total: R$100.00
Produto: Calça Jeans, quantidade: 2, valor total: R$200.00
Produto: Tênis, quantidade: 1, valor total: R$150.00

Esta abordagem funciona, mas percebam que precisamos quebrar cada tupla em 3 partes, e isso é exatamente o que o desempacotamento faz. No lugar da variável item, podemos desempacotar a variável também dentro do laço for. Vejamos como podemos reescrever o código acima utilizando o desempacotamento:

vendas = [("Camiseta", 4, 25.00), ("Calça Jeans", 2, 100.00), ("Tênis", 1, 150.00)]

for produto, quantidade, preco_unitario in vendas:
    valor_total = quantidade * preco_unitario
    print(
        f"Produto: {produto}, quantidade: {quantidade}, valor total: R${valor_total:.2f}"
    )
Produto: Camiseta, quantidade: 4, valor total: R$100.00
Produto: Calça Jeans, quantidade: 2, valor total: R$200.00
Produto: Tênis, quantidade: 1, valor total: R$150.00

Trocamos esse trecho:

for item in vendas:
    produto = item[0]
    quantidade = item[1]
    preco_unitario = item[2]

por esse:

for produto, quantidade, preco_unitario in vendas:

O efeito deles é exatamente o mesmo, mas a segunda forma é mais limpa e mais fácil de entender. Mas observem que só é possível fazer isso porque todos as tuplas da lista tem exatamente 3 elementos. Se houvesse ao menos uma tupla com elementos a mais ou a menos, o código não funcionaria. Observem:

vendas = [
    ("Camiseta", 4, 25.00),
    ("Calça Jeans", 2, 100.00),
    ("Tênis", 1, 150.00),
    # Adição de um item sem preço, somente com 2 elementos (produto e quantidade)
    ("Meias", 5),
]

for produto, quantidade, preco_unitario in vendas:
    valor_total = quantidade * preco_unitario
    print(
        f"Produto: {produto}, quantidade: {quantidade}, valor total: R${valor_total:.2f}"
    )
Produto: Camiseta, quantidade: 4, valor total: R$100.00
Produto: Calça Jeans, quantidade: 2, valor total: R$200.00
Produto: Tênis, quantidade: 1, valor total: R$150.00
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[11], line 9
      1 vendas = [
      2     ("Camiseta", 4, 25.00),
      3     ("Calça Jeans", 2, 100.00),
   (...)
      6     ("Meias", 5),
      7 ]
----> 9 for produto, quantidade, preco_unitario in vendas:
     10     valor_total = quantidade * preco_unitario
     11     print(
     12         f"Produto: {produto}, quantidade: {quantidade}, valor total: R${valor_total:.2f}"
     13     )

ValueError: not enough values to unpack (expected 3, got 2)

O erro ValueError: not enough values to unpack (expected 3, got 2) nos informa justamente que o código esperava 3 elementos para desempacotar em 3 variáveis, mas recebeu apenas 2.

A função enumerate#

Dado que já sabemos sobre desempacotamento, podemos introduzir a função enumerate. Vamos retomar o exemplo anterior:

meses = [
    "Janeiro",
    "Fevereiro",
    "Março",
    "Abril",
    "Maio",
    "Junho",
    "Julho",
    "Agosto",
    "Setembro",
    "Outubro",
    "Novembro",
    "Dezembro",
]

for indice in range(len(meses)):
    numero_mes = indice + 1
    print(f"{meses[indice]} é o {numero_mes}° mês do ano.")
Janeiro é o 1° mês do ano.
Fevereiro é o 2° mês do ano.
Março é o 3° mês do ano.
Abril é o 4° mês do ano.
Maio é o 5° mês do ano.
Junho é o 6° mês do ano.
Julho é o 7° mês do ano.
Agosto é o 8° mês do ano.
Setembro é o 9° mês do ano.
Outubro é o 10° mês do ano.
Novembro é o 11° mês do ano.
Dezembro é o 12° mês do ano.

Quando precisamos acessar o índice e o valor de uma sequencia, a função enumerate é muito útil. Ela retorna uma tupla contendo o índice e o valor do elemento da sequencia. Vejamos o exemplo abaixo:

meses = [
    "Janeiro",
    "Fevereiro",
    "Março",
    "Abril",
    "Maio",
    "Junho",
    "Julho",
    "Agosto",
    "Setembro",
    "Outubro",
    "Novembro",
    "Dezembro",
]

print(list(enumerate(meses)))
[(0, 'Janeiro'), (1, 'Fevereiro'), (2, 'Março'), (3, 'Abril'), (4, 'Maio'), (5, 'Junho'), (6, 'Julho'), (7, 'Agosto'), (8, 'Setembro'), (9, 'Outubro'), (10, 'Novembro'), (11, 'Dezembro')]

A função enumerate é bem simples. Ela recebe uma sequencia e retorna um objeto que contém o índice e o valor do elemento da sequencia. E, como vimos anteriormente, podemos desempacotar essa tupla diretamente no laço for. Ao invés de usar for indice in range(len(meses)), podemos usar for indice, mes in enumerate(meses).

Nota (enumerate transformado em lista)

O resultado da função enumerate por si só já é uma sequencia. Não é necessário transformá-lo em uma lista para usá-lo em um laço for. O motivo de termos transformado no exemplo acima foi puramente para imprimir o resultado dele de uma forma mais visual e útil na tela. Caso contrário seria impresso algo como <enumerate object at 0x7f8e3c7b3b40>, que não é nada útil. Mostramos isso também na função range anteriormente, lembra?

meses = [
    "Janeiro",
    "Fevereiro",
    "Março",
    "Abril",
    "Maio",
    "Junho",
    "Julho",
    "Agosto",
    "Setembro",
    "Outubro",
    "Novembro",
    "Dezembro",
]

for numero_mes, mes in enumerate(meses):
    print(f"{mes} é o {numero_mes}° mês do ano.")
Janeiro é o 0° mês do ano.
Fevereiro é o 1° mês do ano.
Março é o 2° mês do ano.
Abril é o 3° mês do ano.
Maio é o 4° mês do ano.
Junho é o 5° mês do ano.
Julho é o 6° mês do ano.
Agosto é o 7° mês do ano.
Setembro é o 8° mês do ano.
Outubro é o 9° mês do ano.
Novembro é o 10° mês do ano.
Dezembro é o 11° mês do ano.

Só que ainda ficou algo estranho, pois a indexação em Python começa por 0. E, no caso acima, o nosso desejo é que a numeração dos meses comece no 1. Podemos usar um parâmetro da função enumerate para que a sequencia dos índices retornados pela função comece no 1. Vejamos:

meses = [
    "Janeiro",
    "Fevereiro",
    "Março",
    "Abril",
    "Maio",
    "Junho",
    "Julho",
    "Agosto",
    "Setembro",
    "Outubro",
    "Novembro",
    "Dezembro",
]

for numero_mes, mes in enumerate(meses, start=1):
    print(f"{mes} é o {numero_mes}° mês do ano.")
Janeiro é o 1° mês do ano.
Fevereiro é o 2° mês do ano.
Março é o 3° mês do ano.
Abril é o 4° mês do ano.
Maio é o 5° mês do ano.
Junho é o 6° mês do ano.
Julho é o 7° mês do ano.
Agosto é o 8° mês do ano.
Setembro é o 9° mês do ano.
Outubro é o 10° mês do ano.
Novembro é o 11° mês do ano.
Dezembro é o 12° mês do ano.

Saímos desta versão…

for indice in range(len(meses)):
    numero_mes = indice + 1
    print(f"{meses[indice]} é o {numero_mes}° mês do ano.")

… para esta versão…

for numero_mes, mes in enumerate(meses, start=1):
    print(f"{mes} é o {numero_mes}° mês do ano.")

… simplesmente aplicando a função enumerate junto com o conceito de desempacotamento.

Agora eu te pergunto: qual das duas versões ficou mais clara, consisa e fácil de entender?

O laço while#

Diferente do laço for o laço while é um laço de repetiçao no qual não sabemos previamente quantas vezes o código do bloco vai ser executado. Isso porque a execução do laço while é baseada em uma condição. Sim! É o mesmo conceito de condição que vimos no capítulo sobre estrutura condicional, lembra? Em resumo, é a condição que vai determinar se o código do bloco while deve ser executado ou não. É A sintaxe básica do laço while é a seguinte:

while condicao:
    # bloco de código a ser repetido até que a condição seja falsa

Por ser baseado em uma condicional, existe o risco de cairmos em uma situação de loop infinito! Ou seja, o código do bloco while é executado indefinidamente. Por isso, é muito importante que a condição do laço while seja alterada de forma que, em algum momento, ela se torne False. Caso contrário, o código do bloco while será executado indefinidamente. Experimente executar o código abaixo na sua máquina e veja o que acontece (pressione Ctrl+C para interromper a execução):

n = 0
while n < 10:
    print(n)

O pequeno exemplo acima vai imprimir 0 infinitamente. Por que isso acontece? Porque a variável n nunca é alterada dentro do laço while, e ela faz parte da condição. Portanto, a condição n < 10 sempre será verdadeira. Para corrigir isso, precisamos incrementar a variável n a cada iteração. Vejamos:

n = 0
while n < 10:
    print(n)
    # Acréscimo da alteração da condição para que ela seja False em algum momento
    n = n + 1
0
1
2
3
4
5
6
7
8
9

Quando n=10 no código acima, a condição n < 10 passa a ser False e o laço não é executado. Mas isso só acontece porque a cada iteração, acrescentamos uma unidade ao valor de n com a linha n += 1. Se não fizermos a atualização da condição, o código entraria em loop infinito, conforme já vimos. Essa questão da atualização da condição é tão importante que vou repetir na nota abaixo!

Perigo (atualização da condição para evitar loop infinito)

É muito importante que a condição do laço while seja atualizada para que, em algum momento, ela se torne False. Caso contrário, o código do bloco while cairá em loop infinito!

Exemplos reais de aplicação do while#

Todas as vezes que você pensar em executar o seu código repetidas vezes até que uma dada condição seja satisfeita, podemos pensar no laço while. Vamos ver alguns exemplos reais.

Controle de estoque em uma loja#

Imagine que você gerencia uma loja de roupas e precisa monitorar o estoque de camisetas. O objetivo é continuar vendendo camisetas enquanto o estoque for suficiente. Assim que o estoque atingir um limite mínimo (por exemplo, 5 unidades), você decide parar as vendas e encomendar mais.

estoque_camisetas = 20  # Quantidade inicial de camisetas no estoque
limite_minimo = 5  # Limite mínimo para parar as vendas

while estoque_camisetas > limite_minimo:
    print(f"Vendendo uma camiseta. Estoque atual: {estoque_camisetas - 1}")
    estoque_camisetas -= 1

print("Estoque baixo! Encomendar mais camisetas.")
Vendendo uma camiseta. Estoque atual: 19
Vendendo uma camiseta. Estoque atual: 18
Vendendo uma camiseta. Estoque atual: 17
Vendendo uma camiseta. Estoque atual: 16
Vendendo uma camiseta. Estoque atual: 15
Vendendo uma camiseta. Estoque atual: 14
Vendendo uma camiseta. Estoque atual: 13
Vendendo uma camiseta. Estoque atual: 12
Vendendo uma camiseta. Estoque atual: 11
Vendendo uma camiseta. Estoque atual: 10
Vendendo uma camiseta. Estoque atual: 9
Vendendo uma camiseta. Estoque atual: 8
Vendendo uma camiseta. Estoque atual: 7
Vendendo uma camiseta. Estoque atual: 6
Vendendo uma camiseta. Estoque atual: 5
Estoque baixo! Encomendar mais camisetas.

Cobrança de pagamentos pendentes#

Uma empresa de serviços de assinatura precisa enviar lembretes de pagamento a clientes que ainda não pagaram a mensalidade. O sistema verifica continuamente a lista de clientes com pagamentos pendentes e envia lembretes até que todos os pagamentos sejam quitados.

pagamentos_pendentes = 10  # Número inicial de clientes com pagamentos pendentes

while pagamentos_pendentes > 0:
    print(f"Enviando lembrete de pagamento. Clientes pendentes: {pagamentos_pendentes}")
    pagamentos_pendentes -= 1

print("Todos os pagamentos foram recebidos.")
Enviando lembrete de pagamento. Clientes pendentes: 10
Enviando lembrete de pagamento. Clientes pendentes: 9
Enviando lembrete de pagamento. Clientes pendentes: 8
Enviando lembrete de pagamento. Clientes pendentes: 7
Enviando lembrete de pagamento. Clientes pendentes: 6
Enviando lembrete de pagamento. Clientes pendentes: 5
Enviando lembrete de pagamento. Clientes pendentes: 4
Enviando lembrete de pagamento. Clientes pendentes: 3
Enviando lembrete de pagamento. Clientes pendentes: 2
Enviando lembrete de pagamento. Clientes pendentes: 1
Todos os pagamentos foram recebidos.

Cálculo de juros compostos para atiginr um objetivo#

Uma pessoa quer investir um valor inicial e deixá-lo crescer com juros compostos até atingir um objetivo de investimento. O laço while calcula o valor do investimento ao final de cada período de tempo até que o valor desejado seja atingido.

valor_inicial = 1000  # Valor inicial do investimento
objetivo_investimento = 2000  # Valor desejado para o investimento
taxa_juros = 0.05  # Taxa de juros compostos por período
periodos = 0  # Contador de períodos

while valor_inicial < objetivo_investimento:
    valor_inicial += valor_inicial * taxa_juros
    periodos += 1
    print(f"Após {periodos} período(s), o valor do investimento é {valor_inicial:.2f}")

print(
    f"Objetivo de investimento de {objetivo_investimento} alcançado após {periodos} períodos."
)
Após 1 período(s), o valor do investimento é 1050.00
Após 2 período(s), o valor do investimento é 1102.50
Após 3 período(s), o valor do investimento é 1157.62
Após 4 período(s), o valor do investimento é 1215.51
Após 5 período(s), o valor do investimento é 1276.28
Após 6 período(s), o valor do investimento é 1340.10
Após 7 período(s), o valor do investimento é 1407.10
Após 8 período(s), o valor do investimento é 1477.46
Após 9 período(s), o valor do investimento é 1551.33
Após 10 período(s), o valor do investimento é 1628.89
Após 11 período(s), o valor do investimento é 1710.34
Após 12 período(s), o valor do investimento é 1795.86
Após 13 período(s), o valor do investimento é 1885.65
Após 14 período(s), o valor do investimento é 1979.93
Após 15 período(s), o valor do investimento é 2078.93
Objetivo de investimento de 2000 alcançado após 15 períodos.

Percebam que em todos os exemplos acima, dentro do laço while existe uma atualização. No primeiro exemplo, a atualização é feita quando uma camiseta é vendida. No segundo exemplo, a atualização é feita quando um lembrete de pagamento é enviado. E no terceiro exemplo, a atualização é feita quando o valor do investimento é recalculado.

Controle do fluxo de repetição#

Existem alguma intruções que podemos usar para controlar o fluxo de repetição. São elas: break e continue. Vamos entender o que cada uma delas faz.

break#

A instrução break é usado para interromper a execução do laço for ou while. Quando o break é encontrado, o laço é interrompido e o código continua a execução seguindo do que vem em sequencia do bloco interrompido. O break é muito usado dentro de um if para interromper o laço quando uma condição é satisfeita.

Vamos ver um exemplo de funcionamento do break no laço for.

Suponha que você trabalhe no setor de RH de uma empresa e tenha uma lista de currículos para revisar. Você precisa encontrar o primeiro candidato que atenda a todos os requisitos mínimos: ter pelo menos 5 anos de experiência e um diploma universitário. Assim que encontrar um candidato que atenda a esses critérios, você quer parar a revisão, pois já encontrou uma opção adequada para prosseguir com o processo seletivo.

# Lista de currículos, onde cada currículo é representado por um dicionário
curriculos = [
    {"nome": "João", "experiencia": 3, "diploma": True},
    {"nome": "Maria", "experiencia": 6, "diploma": False},
    {"nome": "Carlos", "experiencia": 5, "diploma": True},
    {"nome": "Ana", "experiencia": 4, "diploma": True},
]

# Iterando sobre a lista de currículos
for curriculo in curriculos:
    # Verifica se o currículo atende aos critérios
    print(f"Candidato avaliado: {curriculo['nome']}")
    if curriculo["experiencia"] >= 5 and curriculo["diploma"]:
        candidato_selecionado = curriculo["nome"]
        # Encerra o laço ao encontrar o primeiro candidato adequado
        break

print(f"Candidato selecionado: {curriculo['nome']}")
Candidato avaliado: João
Candidato avaliado: Maria
Candidato avaliado: Carlos
Candidato selecionado: Carlos

Aqui estamos percorrendo todos os currículos na lista de dicionários. Quando determinada condição é atendida (5 anos de experiência e diploma universitário), o break é executado e o laço for é interrompido. Percebam que o último item da lista não é impresso, pois o laço for foi interrompido antes de chegar nele por conta do break.

Podemos usar o break também no laço while. Vamos entender através do exemplo no qual você é um administrador de segurança de um sistema de autenticação que monitora tentativas de login para detectar possíveis ataques de força bruta. O sistema está configurado para bloquear o acesso de um endereço IP após 3 tentativas de login consecutivas falhas. Assim que a terceira tentativa falha é detectada, o sistema deve interromper o processo de verificação e bloquear o IP.

# Dicionário para armazenar o número de tentativas de login por IP
tentativas_login = {"192.168.1.10": 0, "192.168.1.15": 0, "192.168.1.20": 0}

# Lista de tentativas de login simuladas, com IPs que tentam acessar o sistema
tentativas = [
    "192.168.1.10",
    "192.168.1.15",
    "192.168.1.10",
    "192.168.1.20",
    "192.168.1.10",
    "192.168.1.20",
    "192.168.1.15",
    "192.168.1.15",
    "192.168.1.15",
]

# Número máximo de tentativas antes do bloqueio
max_tentativas = 3

# Variável de índice para controlar a posição na lista de tentativas
indice = 0

# Usando um loop while para monitorar as tentativas de login
while indice < len(tentativas):
    ip_atual = tentativas[indice]

    # Incrementa a contagem de tentativas para o IP atual
    tentativas_login[ip_atual] += 1

    # Verifica se o IP atual excedeu o número máximo de tentativas permitidas
    if tentativas_login[ip_atual] >= max_tentativas:
        print(
            f"Alerta: IP {ip_atual} bloqueado após {tentativas_login[ip_atual]} tentativas falhas!"
        )
        # Encerra o loop ao detectar o IP que excedeu o limite de tentativas
        break

    # Incrementa o índice para passar para a próxima tentativa
    indice += 1
Alerta: IP 192.168.1.10 bloqueado após 3 tentativas falhas!

No exemplo acima, o laço while é interrompido quando o mesmo IP atinge 3 tentativas. O break é executado e o laço é interrompido. Percebam que o último print não é executado, pois o laço while foi interrompido antes de chegar nele por conta do break.

No caso específico do while o break pode ser usado para interromper o fluxo e eviar cair em loop infinito.

continue#

Já a instrução continue é usado para pular a iteração atual do laço for ou while e continuar com a próxima iteração. Ou seja, o código que vem após o continue não é executado, e o laço segue para a próxima iteração.

Você trabalha no setor de marketing de uma empresa e é responsável por enviar e-mails promocionais para uma lista de clientes. No entanto, alguns clientes optaram por não receber e-mails de marketing, e suas informações estão marcadas em um sistema com um sinalizador de «opt-out». Você deseja percorrer a lista de clientes e enviar e-mails apenas para aqueles que não marcaram a opção de «opt-out».

# Lista de clientes, onde cada cliente é representado por um dicionário
clientes = [
    {"nome": "João", "email": "joao@example.com", "opt_out": False},
    {"nome": "Maria", "email": "maria@example.com", "opt_out": True},
    {"nome": "Carlos", "email": "carlos@example.com", "opt_out": False},
    {"nome": "Ana", "email": "ana@example.com", "opt_out": True},
    {"nome": "Pedro", "email": "pedro@example.com", "opt_out": False},
]

# Iterando sobre a lista de clientes
for cliente in clientes:
    # Verifica se o cliente optou por não receber e-mails de marketing
    if cliente["opt_out"]:
        # Se o cliente optou por não receber, pula para o próximo cliente
        continue

    # Envia o e-mail de marketing para o cliente
    print(f"Enviando e-mail para: {cliente['nome']} ({cliente['email']})")

print("Processo de envio de e-mails concluído.")
Enviando e-mail para: João (joao@example.com)
Enviando e-mail para: Carlos (carlos@example.com)
Enviando e-mail para: Pedro (pedro@example.com)
Processo de envio de e-mails concluído.

Vamos ver outro exemplo usando o laço while agora. Você é o gerente de um armazém e precisa fazer um controle de estoque diário. A cada dia, você faz a contagem dos itens em diferentes setores do armazém. Alguns setores podem estar em manutenção, e você deseja pular esses setores durante a contagem para evitar erros. O objetivo é continuar contando os itens nos setores disponíveis e ignorar aqueles que estão em manutenção.

# Lista simulada de setores do armazém e seu status
setores = [
    {"nome": "Setor A", "manutencao": False, "estoque": 120},
    {"nome": "Setor B", "manutencao": True, "estoque": 0},
    {"nome": "Setor C", "manutencao": False, "estoque": 200},
    {"nome": "Setor D", "manutencao": True, "estoque": 0},
    {"nome": "Setor E", "manutencao": False, "estoque": 300},
]

# Variável de índice para controlar a posição na lista de setores
indice = 0

# Variável para armazenar o total de estoque contabilizado
total_estoque = 0

# Usando um loop while para percorrer a lista de setores e contar o estoque
while indice < len(setores):
    setor_atual = setores[indice]

    # Verifica se o setor está em manutenção
    if setor_atual["manutencao"]:
        # Se o setor estiver em manutenção, pula para o próximo setor
        print(
            f"{setor_atual['nome']} está em manutenção. Pulando para o próximo setor."
        )
        indice += 1
        continue

    # Adiciona o estoque do setor atual ao total de estoque
    total_estoque += setor_atual["estoque"]
    print(f"Contando estoque no {setor_atual['nome']}: {setor_atual['estoque']} itens.")

    # Incrementa o índice para passar para o próximo setor
    indice += 1

# Resultado final
print(f"Total de estoque contabilizado: {total_estoque} itens.")
Contando estoque no Setor A: 120 itens.
Setor B está em manutenção. Pulando para o próximo setor.
Contando estoque no Setor C: 200 itens.
Setor D está em manutenção. Pulando para o próximo setor.
Contando estoque no Setor E: 300 itens.
Total de estoque contabilizado: 620 itens.

No exemplo acima, temos uma lista chamada setores, onde cada setor é representado por um dicionário com informações sobre o nome do setor, se está em manutenção, e a quantidade de estoque disponível. A variável indice é usada para percorrer a lista de setores. total_estoque é usada para armazenar o total de itens de estoque contabilizados durante o loop. O laço while continua enquanto o índice for menor que o tamanho da lista de setores. Dentro do laço, há uma verificação para saber se o setor atual está em manutenção (chave manutencao é True). Se o setor estiver em manutenção, o comando continue é executado, o que faz com que o loop pule para a próxima iteração sem adicionar o estoque desse setor ao total. Se o setor não estiver em manutenção, o estoque do setor é adicionado à total_estoque, e uma mensagem é impressa confirmando a contagem. Após o loop, uma mensagem final é impressa para indicar o total de estoque contabilizado.

Resumindo: a diferença entre break e continue é que o break interrompe o laço e o continue pula para a próxima iteração. Ambos são muito usados com a estrutura condicional if para realizar a tomada de decisão.

Compreensão de listas, dicionários e conjuntos#

Python oferece uma forma um tanto quanto diferente para unir laços de repetição e estruturas de decisão com estrutras como listas, dicionários e conjuntos.

Compreensões de listas, dicionários e conjuntos permitem que você crie novas sequencias a partir de sequências existentes de uma forma bastante legível. Vamos explorar como utilizar compreensões para cada tipo de coleção, entender suas sintaxes, e ver exemplos práticos de como aplicá-las.

Compreensão de listas#

A compreensão de listas é uma maneira compacta de criar listas. Em vez de usar loops tradicionais como vimos nesse capítulo para preencher uma lista, você pode usar uma única linha de código para gerar elementos a partir de uma sequência existente. Antes, vamos ver o exemplo de como seria sem usar essa técnica.

Uma loja online vende uma variedade de produtos e precisa garantir que todos os itens em seu estoque estejam acima de um nível mínimo para evitar rupturas. O estoque mínimo para cada produto é armazenado em um dicionário chamado estoque_minimo, onde a chave é o nome do produto e o valor é a quantidade mínima exigida. O estoque atual de cada produto está em um dicionário chamado estoque_atual. A loja deseja criar uma lista de produtos que estão abaixo do estoque mínimo.

estoque_atual = {
    'camiseta': 10,
    'calça': 5,
    'jaqueta': 0,
    'meia': 50,
    'sapato': 2
}
estoque_minimo = {
    'camiseta': 15,
    'calça': 10,
    'jaqueta': 5,
    'meia': 30,
    'sapato': 5
}

Como você faria? Usando um laço for e if, poderíamos fazer algo assim:

# Dicionários com o estoque atual e o estoque mínimo dos produtos
estoque_atual = {"camiseta": 10, "calça": 5, "jaqueta": 0, "meia": 50, "sapato": 2}

estoque_minimo = {"camiseta": 15, "calça": 10, "jaqueta": 5, "meia": 30, "sapato": 5}

# Usando um loop for tradicional para encontrar produtos com estoque abaixo do mínimo
produtos_para_repor = []

for produto, quantidade in estoque_atual.items():
    if quantidade < estoque_minimo[produto]:
        produtos_para_repor.append(produto)

print("Produtos que precisam de reposição:", produtos_para_repor)
Produtos que precisam de reposição: ['camiseta', 'calça', 'jaqueta', 'sapato']

Perceram que é necessário primeiro criar a lista vazia, depois percorrer os items em estoque_atual e verificar se o item está abaixo do estoque mínimo. Se estiver, adicionamos o item à lista.

Toda essa lógica pode ser substuída por uma única linha de código usando a compreensão de listas. Vejamos:

# Dicionários com o estoque atual e o estoque mínimo dos produtos
estoque_atual = {"camiseta": 10, "calça": 5, "jaqueta": 0, "meia": 50, "sapato": 2}

estoque_minimo = {"camiseta": 15, "calça": 10, "jaqueta": 5, "meia": 30, "sapato": 5}

# Usando compreensão de listas para encontrar produtos com estoque abaixo do mínimo
produtos_para_repor = [
    produto
    for produto, quantidade in estoque_atual.items()
    if quantidade < estoque_minimo[produto]
]

print("Produtos que precisam de reposição:", produtos_para_repor)
Produtos que precisam de reposição: ['camiseta', 'calça', 'jaqueta', 'sapato']

Trocamos este trecho de código…

produtos_para_repor = []
for produto, quantidade in estoque_atual.items():
    if quantidade < estoque_minimo[produto]:
        produtos_para_repor.append(produto)

… por este outro:

produtos_para_repor = [
    produto
    for produto, quantidade in estoque_atual.items()
    if quantidade < estoque_minimo[produto]
]

E a imagem mostra como os 2 códigos, com e sem compreensão de listas, são equivalentes.

../_images/07-02-compreensao-lista.png

Na imagem acima:

  1. A lista produtos_para_repor é inicializada.

  2. Temos o que vai permanecer na lista ao final, no caso, produto.

  3. O laço for com o desempacotamento de estoque_atual.items() em duas variáveis, produto e quantidade.

  4. O filtro condicional if que verifica se a quantidade é menor que o estoque_minimo[produto].

Notem que o que vai permanecer ao final na lista vem antes do laço for quando usamos compreensão de listas. E a estrutura condicional if vem após o laço for. Lembrando que este é um caso mais completo de compreensão de listas. A estrutura condicional if não é obritarória.

Nota (compreensão de listas e identação)

Como compreensão de listas é uma operação interna feita dentro da própria lista, a identação não é necessária. Nos exemplos acima apenas usamos quebra de linha pra facilitar a leitura, mas poderiamos ter feito tudo em uma única linha, conforme abaixo:

produtos_para_repor = [produto for produto, quantidade in estoque_atual.items() if quantidade < estoque_minimo[produto]]

Compreensão de dicionários#

De forma análoga, a compreensão de dicionários é uma maneira compacta de criar dicionários. Em vez de usar loops tradicionais para preencher um dicionário, você pode usar uma única linha de código para gerar pares de chave-valor a partir de uma sequência existente. Vamos ver um exemplo.

Uma loja deseja criar um dicionário com os preços atualizados dos produtos, aplicando um desconto de 10% aos itens que custam mais de R$20.

produtos = {
    'camiseta': 25.0,
    'calça': 50.0,
    'jaqueta': 100.0,
    'meia': 5.0,
    'sapato': 70.0
}

Novamente, como você faria? Usando um laço for e if, poderíamos fazer algo assim:

produtos = {
    "camiseta": 25.0,
    "calça": 50.0,
    "jaqueta": 100.0,
    "meia": 5.0,
    "sapato": 70.0,
}

produtos_com_desconto = {}

for produto, preco in produtos.items():
    if preco > 20:
        produtos_com_desconto[produto] = preco * 0.9

print("Produtos com desconto:", produtos_com_desconto)
Produtos com desconto: {'camiseta': 22.5, 'calça': 45.0, 'jaqueta': 90.0, 'sapato': 63.0}

Porém, podemos usar compreensão de dicionários e realizar uma operação única de forma direta.

produtos = {
    "camiseta": 25.0,
    "calça": 50.0,
    "jaqueta": 100.0,
    "meia": 5.0,
    "sapato": 70.0,
}

produtos_com_desconto = {
    produto: preco * 0.9 for produto, preco in produtos.items() if preco > 20
}

print("Produtos com desconto:", produtos_com_desconto)
Produtos com desconto: {'camiseta': 22.5, 'calça': 45.0, 'jaqueta': 90.0, 'sapato': 63.0}
../_images/07-02-compreensao-dicionario.png

Na imagem acima:

  1. O dicionário produtos_com_desconto é inicializado.

  2. O conjunto final de pares de chave-valor do dicionário final.

  3. O laço for com o desempacotamento de produto.items() em duas variáveis, produto e preco.

  4. O filtro condicional if que verifica se a preco é maior que 20.

Nota (compreensão de dicionários e identação)

Da mesma forma que compreensão de listas, a identação para compreensão de dicionários não é necessária. Novamente, nos exemplos acima apenas usamos quebra de linha pra facilitar a leitura, mas poderiamos ter feito tudo em uma única linha:

produtos_com_desconto = {produto: preco * 0.9 for produto, preco in produtos.items() if preco > 20}

Compreensão de conjuntos#

Podemos também criar compreensão de conjuntos de forma similar.

Imagine que você tenha uma lista de dicionários, onde cada dicionário representa um cliente com informações como nome, idade, e o país de origem. Queremos identificar todos os países únicos de onde os clientes são provenientes para, por exemplo, realizar uma segmentação de mercado.

clientes = [
    {"nome": "Ana", "idade": 25, "pais": "Brasil"},
    {"nome": "Carlos", "idade": 30, "pais": "Brasil"},
    {"nome": "Marta", "idade": 22, "pais": "Argentina"},
    {"nome": "Jorge", "idade": 29, "pais": "Chile"},
    {"nome": "Lucia", "idade": 35, "pais": "Argentina"},
    {"nome": "Pedro", "idade": 28, "pais": "Brasil"},
    {"nome": "Julia", "idade": 27, "pais": "Chile"},
    {"nome": "Luis", "idade": 31, "pais": "Brasil"},
    {"nome": "Marina", "idade": 26, "pais": "Argentina"},
    {"nome": "Fernando", "idade": 33, "pais": "Chile"},
    {"nome": "Gabriela", "idade": 24, "pais": "Brasil"},
]

Podemos usar compreensão de conjuntos para extrair os países únicos dos clientes que possuem menos de 25 anos.

clientes = [
    {"nome": "Ana", "idade": 25, "pais": "Brasil"},
    {"nome": "Carlos", "idade": 30, "pais": "Brasil"},
    {"nome": "Marta", "idade": 22, "pais": "Argentina"},
    {"nome": "Jorge", "idade": 29, "pais": "Chile"},
    {"nome": "Lucia", "idade": 35, "pais": "Argentina"},
    {"nome": "Pedro", "idade": 28, "pais": "Brasil"},
    {"nome": "Julia", "idade": 27, "pais": "Chile"},
    {"nome": "Luis", "idade": 31, "pais": "Brasil"},
    {"nome": "Marina", "idade": 26, "pais": "Argentina"},
    {"nome": "Fernando", "idade": 33, "pais": "Chile"},
    {"nome": "Gabriela", "idade": 24, "pais": "Brasil"},
]

# Set comprehension para extrair países únicos dos clientes
paises_unicos = {cliente["pais"] for cliente in clientes if cliente["idade"] < 25}

print(paises_unicos)
{'Argentina', 'Brasil'}

Percebam que é bem similar ao que fizemos com compreensão de listas e dicionários. A diferença é que usamos {} para criar um set, e não precisamos de pares de chave-valor como em um dicionário.

Prática#

Agora você já é capaz de resolver 2 exercícios legais para praticar o que aprendeu com contexto real de negócio. Sugiro fazer os exercícios 14 e 15 da lista! Topa? Vamos lá?

Conclusão#

Ao longo deste capítulo, você aprendeu sobre as estruturas de repetição em Python: for e while. O laço for é usado quando sabemos quantas vezes queremos executar um bloco de código, enquanto o laço while é usado quando não sabemos quantas vezes queremos executar um bloco de código, mas sabemos a condição de parada. Além disso, você aprendeu sobre as instruções break e continue para controlar o fluxo de repetição. Por fim, você aprendeu sobre compreensões de listas, dicionários e conjuntos, que são maneiras compactas de criar novas sequências a partir de sequências existentes.

No próximo capítulo vamos aprender em mais detalhes sobre funções em Python. Até lá!