Controle de Fluxo (Condições e Laços)#

Normalmente, o interpretador executa as instruções de um programa sequencialmente, uma por vez. No entanto, as linhas que definem funções e classes são processadas apenas quando essas funções ou classes são chamadas. Além disso, algumas palavras-chave têm a capacidade de modificar a sequência de execução do programa.

Operadores Lógicos#

Em Python, existem três operadores lógicos (booleanos):

  • or (ou)

  • and (e)

  • not (não).

Os operadores or e and trabalham com dois operandos e retornam valores lógicos, como True (verdadeiro) ou False (falso). Já o operador not recebe apenas um operando e retorna o valor lógico inverso. A tabela verdade a seguir mostra os resultados desses operadores para diferentes combinações de operandos.

x

y

not x

x or y

x and y

True

True

False

True

True

True

False

False

True

False

False

True

True

True

False

False

False

True

False

False

O operador or retorna False apenas quando ambos os operandos são falsos, por isso, em termos de desempenho, Python avalia o segundo operando apenas se o primeiro for False. Por sua vez, o operador and retorna True somente quando os dois operandos são verdadeiros, então Python só verifica o segundo operando se o primeiro for True. Esse comportamento, conhecido como «curto-circuito», pode ser usado para otimizar o código, colocando operações menos custosas à esquerda desses op

Operadores Relacionais#

O(a) usuário(a) de Python tem à disposição oito operadores de comparação (ou operadores relacionais), conforme apresentado na tabela abaixo. Os dois últimos operadores são casos especiais de verificação de igualdade e diferença de objetos, que serão abordados mais adiante no livro. Também veremos que, exceto para números, objetos de tipos diferentes nunca serão considerados iguais.

Operador

Significado

<, >

menor/maior que

<=, >=

menor/maior ou igual a

==, !=

igual/diferente

is, is not

igualdade/diferença de objetos

As comparações podem ser encadeadas de forma livre, permitindo, por exemplo, verificar se o valor de uma variável y está dentro do intervalo (0, 4] da seguinte maneira:

y = 2

0 < y <= 4
True

O código acima é equivalente ao código abaixo, que segue uma abordagem mais comum em outras linguagens de programação. No entanto, no formato encadeado apresentado anteriormente, a variável y é avaliada apenas uma vez, resultando em um código mais eficiente. Além disso, como as comparações envolvem um and, a segunda parte (y <= 4) só será avaliada se a primeira (0 < y) for True.

0 < y and y <= 4
True

Condições (Instruções If)#

Os operadores condicionais if (se), elif (senão se) e else (senão) são empregados para criar blocos de código que serão executados apenas quando determinadas condições forem atendidas. Esses operadores, portanto, são blocos de controle de fluxo, já que alteram a sequência de execução do programa, como ilustrado no fluxograma abaixo.

Descrição da Imagem

O fluxograma acima representa uma entrevista de emprego onde a única pergunta é se o candidato tem conhecimento em Python ou não. Se a resposta for positiva, a pessoa é contratada de imediato. Caso contrário, continuará sem emprego. Esse cenário pode ser descrito da seguinte maneira:

Em Python, isso seria escrito da seguinte forma:

knows_python = True

if knows_python == True:
    situation = 'hired'
else:
    situation = 'unemployed'

print(situation)
hired

Observe que para utilizar a condicional acima fez-se uso da indentação.

A indentação no python define o que chamos de bloco de código.

Blocos são seções de código que executam uma tarefa ou um conjunto de tarefas relacionadas. Essas seções podem definir operações condicionais, iterações (operações repetitivas), funções e tipos de dados. Além disso, blocos podem ser aninhados, ou seja, é possível incluir blocos dentro de outros, o que corresponde à definição de sub-tarefas.

A principal diferença entre Python e outras linguagens, como R, C, Fortran e Java, é a forma como os blocos de código são representados. Em Python, utilizam-se níveis de indentação para definir os blocos, eliminando a necessidade de chaves {} para marcar o início e o fim dos mesmos ou palavras-chave de início (begin) e fim (end).

A utilização de indentação para definir a estrutura do código oferece diversos benefícios, incluindo:

  1. Menos necessidade de padrões adicionais: a indentação será sempre de 4 espaços, e a IDE utilizada para escrever o código garante essa consistência automaticamente;

  2. Uniformidade no estilo de indentação: códigos de diferentes fontes são obrigados a seguir o mesmo estilo de indentação;

  3. Redução de esforço: não é preciso se preocupar com a definição de padrões para chaves ou indentação;

  4. Código mais limpo: a indentação contribui para uma aparência mais clara e organizada do código;

  5. Execução condicional: o código só funcionará se a indentação estiver correta, o que significa que, se o código parece correto, ele está de fato correto;

  6. Evita confusão em blocos aninhados: não há risco de confundir os escopos de blocos de código dentro de estruturas aninhadas.

Retornando ao exemplo, observe que a variável situation não foi definida antes do bloco condicional. Isso não gera erro porque existem apenas dois fluxos possíveis para o código: knows_python == True ou knows_python == False. No entanto, se uma variável não for definida em todos os fluxos possíveis, pode ocorrer um erro. Por exemplo, imagine que, se a pessoa for contratada, ela receberá um salário de R$6000,00. Se a pessoa não souber Python e tentarmos acessar o salário, isso causará um erro.

knows_python = False

if knows_python == True:
    situation = 'hired'
    salary = 6000
else:
    situation = 'unemployed'

salary
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[4], line 9
      6 else:
      7     situation = 'unemployed'
----> 9 salary

NameError: name 'salary' is not defined

Note que a linha que define o bloco condicional começa com o operador if, seguido por uma expressão, e termina com dois pontos. A linha que define o caso contrário contém apenas else:. Em geral, o if verifica se a expressão que o acompanha é True ou False e executa o bloco de código associado somente quando o resultado da expressão for True. Vale observar que, como o operador if já testa se a expressão é True, o código acima é redundante e pode ser simplificado para o seguinte teste: (knows_python == True) == True. Ou seja, a condição poderia ser escrita de maneira mais simples assim:

knows_python = True

if knows_python:
    situation = 'hired'
else:
    situation = 'unemployed'

print(situation)
hired

Ao eliminar a necessidade de escrever explicitamente o teste de igualdade, Python torna o código mais próximo da linguagem natural.

Como vimos, o código acima verifica se o valor de uma variável booleana é verdadeiro ou falso. No entanto, de maneira curiosa, Python também permite testar se True ou False se aplicam a outros tipos de valores, o que, embora não seja tão intuitivo, acaba sendo muito útil. Os valores que são avaliados como False incluem None (nulo), o valor 0 de qualquer tipo numérico e coleções vazias, como '', (), [], {}, set(), range(0). Todos os outros valores são avaliados como True.

if '':
    result = 'teste True'
else:
    result = 'teste False'

result
'teste False'
if None:
    result = 'teste True'
else:
    result = 'teste False'

result    
'teste False'

Isso permite testar facilmente se uma lista tem ou elementos ou não. Por exemplo:

if []:
    result = 'lista com elementos'
else:
    result = 'lista vazia'

result
'lista vazia'

A expressão associada ao if pode ser tão complexa quanto necessário, utilizando operadores e chamadas de funções, que abordaremos mais adiante. O código abaixo verifica se a média de três valores é maior que 8 (vale notar que a expressão aritmética é avaliada antes da comparação):

x1 = 1
x2 = 3
x3 = 6

if (x1 + x2 + x3) / 3 > 3:
    result = 'A média é maior do que 8'
else:
    result = 'A média é menor do que 8'

print(result)
A média é maior do que 8

Múltiplas condições#

Os exemplos anteriores testaram apenas situações binárias, ou seja, com duas respostas possíveis. Para verificar mais condições dentro de um único bloco condicional, existem duas abordagens:

  • usar o comando elif para testar múltiplas situações

  • utilizar ifs aninhados.

O comando elif funciona como o else if em outras linguagens de programação e pode ser interpretado como senão se.

Vamos imaginar que, na entrevista de emprego que simulamos anteriormente, não seja perguntado apenas se a pessoa sabe ou não Python, mas quais linguagens de programação ela domina.

  1. Se a pessoa não souber nenhuma linguagem, continuará desempregada.

  2. Caso ela domine Python e R, será contratada como cientista de dados.

  3. Se souber outras linguagens, incluindo Python ou R (mas não as duas), será contratada como desenvolvedora de software.

Essa entrevista poderia ser codificada em Python da seguinte maneira:

linguagens = ['Python', 'R']

if not linguagens:
    situação = 'desempregada'
elif 'Python' in linguagens and 'R' in linguagens:
    situação = 'ciêntista de dados'
else:
    situação = 'desenvolvedor de softwares'

print(situação)
ciêntista de dados
linguagens = ['Python', 'JavaScript', 'C', 'Fortran']

if not linguagens:
    situação = 'desempregada'
elif 'Python' in linguagens and 'R' in linguagens:
    situação = 'ciêntista de dados'
else:
    situação = 'desenvolvedor de softwares'

print(situação)
desenvolvedor de softwares

Vamos imaginar que a empresa vai classificar o novo cientista de dados de acordo com sua experiência profissional em três níveis: júnior, pleno ou sênior. As condições para cada nível seriam:

  1. O candidato será considerado cientista de dados júnior se tiver menos de 3 anos de experiência.

  2. Se o candidato tiver entre 3 e 5 anos de experiência, será classificado como cientista de dados pleno.

  3. Para candidatos com 5 anos ou mais de experiência, a classificação será cientista de dados sênior.

O código Python para essa situação seria:

linguagens = ['Python', 'R']
experiencia = 4

if not linguagens:
    situacao = 'desempregado'
elif 'Python' in linguagens and 'R' in linguagens:
    if experiencia < 3:
        situacao = 'cientista de dados júnior'
    elif experiencia < 5:
        situacao = 'cientista de dados pleno'
    else:
        situacao = 'cientista de dados sênior'
else:
    situacao = 'desenvolvedor de softwares'

situacao
'cientista de dados pleno'

if permite a ramificação. O Python não possui uma instrução select/case como algumas outras linguagens, mas if, elif e else podem reproduzir qualquer funcionalidade de ramificação que você possa precisar.

Exercício Rapído

Dado um valor de x, escreva um programa que imprima «negativo» se x for menor que 0, «zero» se x for igual a 0 e «positivo» caso contrário.

Solução
x = 0

if x < 0:
    print("negativo")
elif x == 0:
    print("zero")
else:
    print("positivo")

Loops (Laços)#

Um simples laço while — note a indentação para denotar o bloco que faz parte do laço.

Aqui também usamos o operador compacto +=: n += 1 é o mesmo que n = n + 1.

Os blocos condicionais permitem alterar o fluxo do programa, mas são executados apenas uma vez. Para repetir a execução de um bloco de código, utilizamos os operadores de laço, como while (enquanto) e for (para cada).

O comando while#

O comando while executa um bloco de código enquanto a expressão associada for avaliada como True, ou seja, ele age como um if repetido.

Deve-se ter cuidado ao usá-lo, pois se a expressão nunca for avaliada como False, o código ficará preso em um laço infinito.

n = 0
while n < 10:
    print(n)
    n += 1
0
1
2
3
4
5
6
7
8
9

Podemos também usar o comando while para navegar entre elementos em uma lista.

Considere uma lista l = [3, 1, 3, 5, 6] com números inteiros, o programa abaixo remove os elementos dessa lista um a um, somando seus valores em uma variável total até que a lista esteja vazia. Ao final, o valor de total deve ser igual à soma de todos os elementos da lista.

A operação pop(0) remove o primeiro elemento da lista (índice 0) e o retorna, nesse caso atribuindo-o à variável value.


l = [3, 1, 3, 5, 6]

total = 0

while l:
    valor = l.pop(0)
    total = total + valor

print(l,total)
[] 18

Observe que a expressão testada pelo while é simplesmente a lista l. Como vimos anteriormente, uma lista é considerada False apenas quando está vazia, portanto, o laço while continuará a execução até que a lista l seja esvaziada. Assim como ocorre com o if, o while também pode ter um «caso contrário», ou seja, um bloco de código associado ao operador else, que será executado apenas quando a expressão vinculada ao while for avaliada como False.

l = [3, 1, 3, 5, 6]

total = 0
mensagem = 'A lista não está vazia'

while l:
    valor = l.pop(0)
    total = total + valor
else:
    mensagem = 'A lista está vazia'
    
print(mensagem)
A lista está vazia

A execução de um laço while pode ser controlada por dois comandos:

  • break

  • continue.

Break#

O comando break encerra o laço imediatamente, ou seja, o código dentro do bloco else não será executado.

No exemplo abaixo, se o valor 5 for encontrado enquanto a lista está sendo processada, a execução irá parar.

l = [3, 1, 3, 5, 6]

total = 0
mensagem = 'A lista não está vazia'

while l:
    valor = l.pop(0)
    if valor == 5:
        break

    total = total + valor
else:
    mensagem = 'A lista está vazia'
    
print(mensagem, total)
A lista não está vazia 7

Perceba que a variável mensagem não foi alterada, pois seu valor só é atualizado dentro do bloco else. Além disso, a variável total assume o valor 7, que é a soma de 3 + 1 + 3.

Continue#

O comando continue, por sua vez, faz com que o while retorne imediatamente para a verificação da expressão associada, pulando os passos seguintes dentro do seu bloco de código.

No exemplo abaixo, se o valor 5 for encontrado, ele será ignorado, o que resulta em a variável total conter o valor 13 (3 + 1 + 3 + 6). A execução do bloco else não é alterada.

l = [3, 1, 3, 5, 6]

total = 0
mensagem = 'A lista não está vazia'

while l:
    valor = l.pop(0)
    if valor == 5:
        continue

    total = total + valor
else:
    mensagem = 'A lista está vazia'
    
print(mensagem, total)
A lista está vazia 13

O comando for#

O comando for é utilizado para percorrer os elementos de uma coleção (como listas, tuplas ou strings). Ele pode ser entendido como:

O bloco de código associado é executado uma vez para cada item da coleção, na ordem em que os elementos são retornados. Quando não houver mais elementos para iterar, as iterações se encerram e, se houver um bloco else relacionado ao for, ele será executado.

O comando for é frequentemente utilizado em conjunto com coleções do tipo range, que gera uma sequência de valores. Por exemplo, para somar todos os elementos no intervalo [0, 4), podemos usar:

total = 0

for i in range(4):
    total += i

total
6

Podemos simplesmente acessar os itens da lista

alist = [1, 2.0, "três", 4]
for a in alist:
    print(a)
1
2.0
três
4

Para somar todos os elementos de uma lista, pode-se fazer (note que a lista continua preenchida):

l = [2, 1, 4, 8, 6]
total = 0

for i in l:
    total += i

print(total)
print(l)
21
[2, 1, 4, 8, 6]

A cada iteração do código acima, o próximo elemento retornado pela lista é atribuído à variável i. Por isso, ao final das iterações, i terá o valor do último elemento que foi atribuído a ela.

i
6

Naturalmente, se o laço não chegar a executar, a variável nunca receberá um valor. Por exemplo:

total = 0

for j in []:
    total += 0

j
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[22], line 6
      3 for j in []:
      4     total += 0
----> 6 j

NameError: name 'j' is not defined

Podemos também interagir com strings. Quando fazemos isso, acessamos cada caractere de forma sequencial

for c in "isto é uma string":
    print(c)
i
s
t
o
 
é
 
u
m
a
 
s
t
r
i
n
g

Podemos combinar laços e testes if para realizar lógicas mais complexas, como sair do laço quando você encontra o que está procurando.

n = 0
for a in alist:
    if a == "três":
        break
    else:
        n += 1

print(n)
2

(No entanto, para esse exemplo, há uma maneira mais simples.)

alist.index("três")
2

Para dicionários, você também pode iterar sobre os elementos.

my_dict = {"chave1":1, "chave2":2, "chave3":3}

for k, v in my_dict.items():
    print("chave = {}, valor = {}".format(k, v))    # notice how we do the formatting here
chave = chave1, valor = 1
chave = chave2, valor = 2
chave = chave3, valor = 3
for k in my_dict:
    print(k, my_dict[k])
chave1 1
chave2 2
chave3 3

Às vezes, queremos iterar sobre um elemento de uma lista e saber seu índice — enumerate() ajuda aqui:

for n, a in enumerate(alist):
    print(n, a)
0 1
1 2.0
2 três
3 4

A função zip() nos permite iterar sobre dois iteráveis ao mesmo tempo. Assim zip(a, b) atuará como uma lista, onde cada elemento é uma tupla com um item de a e o elemento correspondente de b.

a = [1, 2, 3, 4, 5, 6, 7, 8]
b = ["a", "b", "c", "d", "e", "f", "g", "h"]


for x,y in zip(a,b):
    print(x,y)
1 a
2 b
3 c
4 d
5 e
6 f
7 g
8 h
list(zip(a,b))
[(1, 'a'),
 (2, 'b'),
 (3, 'c'),
 (4, 'd'),
 (5, 'e'),
 (6, 'f'),
 (7, 'g'),
 (8, 'h')]

Exercício Rápido:

A função .split() em uma string pode dividi-la em palavras (separando por espaços).

Usando .split(), itere sobre as palavras na string

a = "O rato roeu a roupa do rei de roma"

e imprima uma palavra por linha.

Compreensão de listas#

A compreensão de listas (list comprehension) é uma técnica que possibilita a criação de novas coleções de maneira compacta, a partir de operações aplicadas a cada item de outras coleções.

Por exemplo, o código a seguir cria uma lista com os quadrados dos números em range(10), utilizando o for, conforme já discutido anteriormente:

xaoquadrado = []
for x in range(10):
    xaoquadrado.append(x**2)
    
print(xaoquadrado)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

A mesma lista pode ser criada usando compreensão de listas como segue:

xaoquadrado = [x**2 for x in range(10)]

print(xaoquadrado)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

A compreensão de listas utiliza um for para atribuir cada valor de range à variável x, cujo quadrado é então adicionado à nova lista squares. Após a conclusão das iterações, a variável x terá o último valor que lhe foi atribuído.

x
9

Não é necessário que a coleção gerada pela compreensão de listas tenha o mesmo número de elementos que a coleção original. Por exemplo, o código abaixo calcula apenas os quadrados dos elementos pares (observe o if dentro do for):

xaoquadrado = [x**2 for x in range(10) if x % 2 == 0]

print(xaoquadrado)
[0, 4, 16, 36, 64]

Também é possível utilizar o operador else dentro de uma compreensão de listas. Nesse caso, o if e o else devem preceder o for, formando uma estrutura conhecida como operador ternário:

if <expressão> else <outro_valor>

xaoquadrado = [x**2 if x % 2 == 0 else x for x in range(10)]

print(xaoquadrado)
[0, 1, 4, 3, 16, 5, 36, 7, 64, 9]

Observe que o if só aparece antes do for quando faz parte de um operador ternário. Ou seja, um if sem o else antes do for resulta em um erro de sintaxe:

[x**2 if x % 2 == 0 for x in range(10)]
  Cell In[36], line 1
    [x**2 if x % 2 == 0 for x in range(10)]
     ^
SyntaxError: expected 'else' after 'if' expression