Avaliação de curto-circuito em Python – Operadores booleanos

A linguagem Python suporta os seguintes operadores booleanos:

Operador Significado
not negação unária
and E condicional
or OU condicional

Os operadores and e or são operadores em curto-circuito, ou seja, o segundo operando só é avaliado se o resultado não puder ser determinado com base apenas no valor do primeiro operando. Confuso? Vamos ver alguns exemplos neste artigo.

Visão geral

and

Considere que estamos procurando em uma lista dados um determinado valor 'X' e que queremos ter o índice desse valor. As listas em Python possuem um método para isso, mas aqui o objetivo é entender o conceito, então vamos resolver com o mais básico da linguagem. Em um primeiro momento, vamos considerar que o valor procurado efetivamente está na lista:

dados = ['A', 'C', 'D', 'X', 'Z']

Uma forma simples é com um loop while:

indice = 0
while indice < len(dados) and dados[indice] != 'X':
    indice += 1
print(indice)
3

Repare no código anterior que primeiro checamos se o índice é menor que o tamanho da lista e depois verificamos se a posição avaliada é diferente do valor desejado. Vamos ver o que ocorre se mudarmos a ordem da comparação:

indice = 0
while dados[indice] != 'X' and indice < len(dados):
    indice += 1
print(indice)
3

Observe que, aparentemente, não há diferença. Mas o que irá ocorrer se a lista não possuir o valor procurado?

Vamos redefinir nossa lista sem o valor 'X':

dados = ['A', 'C', 'D', 'Z']

Vamos fazer novamente nosso loop verificando antes se o índice é menor que o tamanho da lista:

indice = 0
while indice < len(dados) and dados[indice] != 'X':
    indice += 1
print(indice)
4

Obtivemos o valor 4 para indice pois toda a lista foi percorrida e não se encontrou o valor desejado. Assim, todos os índices válidos foram percorridos e, quando se verificou que a primeira comparação (primeiro operando de and) não era válida, o loop foi interrompido. Repare que o índice 4 não faz sentido nessa lista, pois o maior índice possível é 3 (lembre-se que sequências em Python começam em índice zero).

Agora vamos ver o que ocorre trocando a ordem dos operandos:

indice = 0
while dados[indice] != 'X' and indice < len(dados):
    indice += 1
print(indice)
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-6-d88901ad107a> in <module>
      1 indice = 0
----> 2 while dados[indice] != 'X' and indice < len(dados):
      3     indice += 1
      4 print(indice)

IndexError: list index out of range

Um erro ocorre pois, quando temos 4 sendo associado à indice, o primeiro operando é dados[4] != 'X'. Como o índice 4 não é válido, temos um IndexError. Observe que o Python não avaliou o segundo operando, que iria impedir o erro, caracterizando o comportamento conhecido como curto-circuito.

Esse comportamento pode ser utilizado, como vimos nesse exemplo, para evitar possíveis erros na avaliação do segundo operando. Avaliar primeiro se o índice é menor que o comprimento impede que ocorra IndexError.

Em que se baseia esse comportamento? Em lógica booleana básica. O operador and, de conjunção lógica, só pode ter resultado True se ambos os operandos forem True. Assim, o primeiro operando tendo valor False já é o suficiente para que o loop seja interrompido. Caso tenha ficado confuso, dê uma olhada em tabelas-verdade, inclusive já fiz um pequeno programa Python para construção de tabelas-verdade que você pode conferir aqui. Fiz também um material ensinando um pouco de lógica booleana com exercícios para praticar que pode ser acessado aqui.

Para deixar completo o artigo, a tabela verdade para o operador and, gerada pelo projeto Truth Table Generator é a seguinte:

+--------------+--------------+-----------------------------+
|  operando_1  |  operando_2  |  operando_1 and operando_2  |
|--------------+--------------+-----------------------------|
|     True     |     True     |            True             |
|     True     |    False     |            False            |
|    False     |     True     |            False            |
|    False     |    False     |            False            |
+--------------+--------------+-----------------------------+

Observe que, apenas quando os dois operandos são True, o resultado será True. Assim, se o primeiro operando for False não há necessidade de avaliar o segundo. É esse o princípio por trás do comportamento de curto-circuito.

or

Vamos ver exemplos para o operador or. Para entender esses exemplos, precisamos saber que o interpretador da linguagem considera como False os seguintes casos:

print(bool(False))  # a própria palavra reservada False
print(bool(None))   # a palavra reservada None
print(bool(0))      # o inteiro 0 (zero)
print(bool(""))     # uma string vazia
print(bool(()))     # uma tupla vazia
print(bool([]))     # uma lista vazia
print(bool({}))     # um dicionário vazio
False
False
False
False
False
False
False

Caso queira uma visão mais histórica desse comportamento, recomendo ver a PEP 285, que implementou o tipo bool na linguagem Python.

Vamos então considerar as duas situações seguintes:

apelido = ""


def funcao_executada():
    return "Executou a função"


print(apelido or funcao_executada())
Executou a função
apelido = "Chico"

print(apelido or funcao_executada())
Chico

Novamente, os comportamentos derivam de lógida booleana. O operador or, de disjunção lógica, só pode ter resultado False se ambos os operandos forem False. Assim, o primeiro operando tendo valor True já é o suficiente para que o loop seja interrompido. Por isso, no segundo caso, onde apelido não é uma string vazia, seu valor lógico é True e a função não é executada.

Para deixar completo o artigo, a tabela verdade para o operador or, gerada pelo projeto Truth Table Generator é a seguinte:

+--------------+--------------+----------------------------+
|  operando_1  |  operando_2  |  operando_1 or operando_2  |
|--------------+--------------+----------------------------|
|     True     |     True     |            True            |
|     True     |    False     |            True            |
|    False     |     True     |            True            |
|    False     |    False     |           False            |
+--------------+--------------+----------------------------+

Compare a tabela com o que foi escrito para entender o comportamento.

Resumo do comportamento

Operação Resultado Observações
x or y Se x é False, então y, senão x y é executado apenas se x é False
x and y Se x é False, então x, senão y y é executado apenas se x é True
not x Se x é True, então False, senão True not tem menor prioridade do que operadores não booleanos

Gostou desse artigo? Ele faz parte do Python Drops, um conjunto de posts mais curtos voltados para fundamentos falando sobre alguns aspectos da linguagem Python e de programação em geral. Você pode ler mais desses artigos buscando a tag “drops” aqui no site. Até a próxima!

Compartilhe:

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Rolar para cima