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 ande 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 Truee Falserepresenta o que bool(left-hand-side)retorna, eles não precisam ser Trueou False, eles apenas precisam retornar Trueou Falsequando boolsão chamados (1).
Portanto, no pseudo-código (!), As funções ande orfuncionam 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 andou orporque são palavras-chave. Além disso, você nunca deve usar "avaliar" ou if bool(...).
Customizando o comportamento de suas próprias classes
Essa boolchamada implícita pode ser usada para personalizar como suas classes se comportam and, ore 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 boolchamada 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 boolchamada aos operandos deve retornar Trueou Falsenã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, andna verdade, retorna o primeiro operando se o primeiro operando for avaliado Falsee, se for avaliado True, retornará o segundo operando:
>>> x1
Test(10)
>>> x2
Test(False)
Da mesma forma, ormas 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 ifdeclaração if, também implicitamente chamará boolo resultado. Portanto, esses pontos mais delicados podem não ser relevantes para você.