Estou recebendo um erro no condicional IF. O que estou fazendo errado?
A razão pela qual você obtém um SyntaxError
é que não há &&
operador no Python. Da mesma forma ||
e não!
são operadores Python válidos .
Alguns dos operadores que você conhece de outros idiomas têm um nome diferente no Python. Os operadores lógicos &&
e ||
são realmente chamados and
e or
. Da mesma forma, o operador de negação lógica !
é chamado not
.
Então você pode escrever:
if len(a) % 2 == 0 and len(b) % 2 == 0:
ou até:
if not (len(a) % 2 or len(b) % 2):
Algumas informações adicionais (que podem ser úteis):
Resumi o operador "equivalentes" nesta tabela:
+------------------------------+---------------------+
| Operator (other languages) | Operator (Python) |
+==============================+=====================+
| && | and |
+------------------------------+---------------------+
| || | or |
+------------------------------+---------------------+
| ! | not |
+------------------------------+---------------------+
Veja também a documentação do Python: 6.11. Operações booleanas .
Além dos operadores lógicos, o Python também possui operadores bit a bit / binários:
+--------------------+--------------------+
| Logical operator | Bitwise operator |
+====================+====================+
| and | & |
+--------------------+--------------------+
| or | | |
+--------------------+--------------------+
Não há negação bit a bit no Python (apenas o operador inverso bit a bit ~
- mas isso não é equivalente a not
).
Veja também 6.6. Operações aritméticas unárias e bit a bit / binárias e 6.7. Operações aritméticas binárias .
Os operadores lógicos (como em muitos outros idiomas) têm a vantagem de estar em curto-circuito. Isso significa que se o primeiro operando já define o resultado, o segundo operador não é avaliado.
Para mostrar isso, uso uma função que simplesmente pega um valor, imprime e retorna novamente. Isso é útil para ver o que é realmente avaliado devido às instruções de impressão:
>>> def print_and_return(value):
... print(value)
... return value
>>> res = print_and_return(False) and print_and_return(True)
False
Como você pode ver, apenas uma instrução de impressão é executada; portanto, o Python nem sequer olhou para o operando certo.
Este não é o caso dos operadores binários. Aqueles sempre avaliam os dois operandos:
>>> res = print_and_return(False) & print_and_return(True);
False
True
Mas se o primeiro operando não for suficiente, é claro que o segundo operador será avaliado:
>>> res = print_and_return(True) and print_and_return(False);
True
False
Para resumir isso, aqui está outra tabela:
+-----------------+-------------------------+
| Expression | Right side evaluated? |
+=================+=========================+
| `True` and ... | Yes |
+-----------------+-------------------------+
| `False` and ... | No |
+-----------------+-------------------------+
| `True` or ... | No |
+-----------------+-------------------------+
| `False` or ... | Yes |
+-----------------+-------------------------+
O True
e False
representa o que bool(left-hand-side)
retorna, eles não precisam ser True
ou False
, eles apenas precisam retornar True
ou False
quando bool
são chamados (1).
Portanto, no pseudo-código (!), As funções and
e or
funcionam assim:
def and(expr1, expr2):
left = evaluate(expr1)
if bool(left):
return evaluate(expr2)
else:
return left
def or(expr1, expr2):
left = evaluate(expr1)
if bool(left):
return left
else:
return evaluate(expr2)
Note que este é pseudo-código e não código Python. No Python, você não pode criar funções chamadas and
ou or
porque são palavras-chave. Além disso, você nunca deve usar "avaliar" ou if bool(...)
.
Customizando o comportamento de suas próprias classes
Essa bool
chamada implícita pode ser usada para personalizar como suas classes se comportam and
, or
e not
.
Para mostrar como isso pode ser personalizado, eu uso essa classe, que print
é outra coisa para rastrear o que está acontecendo:
class Test(object):
def __init__(self, value):
self.value = value
def __bool__(self):
print('__bool__ called on {!r}'.format(self))
return bool(self.value)
__nonzero__ = __bool__ # Python 2 compatibility
def __repr__(self):
return "{self.__class__.__name__}({self.value})".format(self=self)
Então, vamos ver o que acontece com essa classe em combinação com esses operadores:
>>> if Test(True) and Test(False):
... pass
__bool__ called on Test(True)
__bool__ called on Test(False)
>>> if Test(False) or Test(False):
... pass
__bool__ called on Test(False)
__bool__ called on Test(False)
>>> if not Test(True):
... pass
__bool__ called on Test(True)
Se você não possui um __bool__
método, o Python também verifica se o objeto possui um __len__
método e se ele retorna um valor maior que zero. Isso pode ser útil saber caso você crie um contêiner de sequência.
Veja também 4.1. Teste do valor da verdade .
Matrizes e subclasses NumPy
Provavelmente um pouco além do escopo da pergunta original, mas no caso de você estar lidando com matrizes ou subclasses NumPy (como Pandas Series ou DataFrames), a bool
chamada implícita aumentará o temido ValueError
:
>>> import numpy as np
>>> arr = np.array([1,2,3])
>>> bool(arr)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
>>> arr and arr
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
>>> import pandas as pd
>>> s = pd.Series([1,2,3])
>>> bool(s)
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
>>> s and s
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
Nesses casos, você pode usar a lógica e a função do NumPy, que executa um elemento and
(ou or
):
>>> np.logical_and(np.array([False,False,True,True]), np.array([True, False, True, False]))
array([False, False, True, False])
>>> np.logical_or(np.array([False,False,True,True]), np.array([True, False, True, False]))
array([ True, False, True, True])
Se você está lidando apenas com matrizes booleanas, também pode usar os operadores binários com o NumPy, eles realizam comparações entre elementos (mas também binárias):
>>> np.array([False,False,True,True]) & np.array([True, False, True, False])
array([False, False, True, False])
>>> np.array([False,False,True,True]) | np.array([True, False, True, False])
array([ True, False, True, True])
(1)
Que a bool
chamada aos operandos deve retornar True
ou False
não está completamente correta. É apenas o primeiro operando que precisa retornar um booleano em seu __bool__
método:
class Test(object):
def __init__(self, value):
self.value = value
def __bool__(self):
return self.value
__nonzero__ = __bool__ # Python 2 compatibility
def __repr__(self):
return "{self.__class__.__name__}({self.value})".format(self=self)
>>> x = Test(10) and Test(10)
TypeError: __bool__ should return bool, returned int
>>> x1 = Test(True) and Test(10)
>>> x2 = Test(False) and Test(10)
Isso porque, and
na verdade, retorna o primeiro operando se o primeiro operando for avaliado False
e, se for avaliado True
, retornará o segundo operando:
>>> x1
Test(10)
>>> x2
Test(False)
Da mesma forma, or
mas apenas o contrário:
>>> Test(True) or Test(10)
Test(True)
>>> Test(False) or Test(10)
Test(10)
No entanto, se você usá-los em uma if
declaração if
, também implicitamente chamará bool
o resultado. Portanto, esses pontos mais delicados podem não ser relevantes para você.