Ricevo un errore nel condizionale IF. Che cosa sto facendo di sbagliato?
La ragione per cui si ottiene a SyntaxError
è che non esiste un &&
operatore in Python. Allo stesso modo ||
e non!
sono operatori Python validi .
Alcuni degli operatori che potresti conoscere da altre lingue hanno un nome diverso in Python. Gli operatori logici &&
e ||
sono effettivamente chiamati and
e or
. Allo stesso modo !
viene chiamato l' operatore di negazione logicanot
.
Quindi potresti semplicemente scrivere:
if len(a) % 2 == 0 and len(b) % 2 == 0:
o anche:
if not (len(a) % 2 or len(b) % 2):
Alcune informazioni aggiuntive (che potrebbero tornare utili):
Ho riassunto gli operatori "equivalenti" in questa tabella:
+------------------------------+---------------------+
| Operator (other languages) | Operator (Python) |
+==============================+=====================+
| && | and |
+------------------------------+---------------------+
| || | or |
+------------------------------+---------------------+
| ! | not |
+------------------------------+---------------------+
Vedi anche la documentazione di Python: 6.11. Operazioni booleane .
Oltre agli operatori logici, Python ha anche operatori bit a bit / binari:
+--------------------+--------------------+
| Logical operator | Bitwise operator |
+====================+====================+
| and | & |
+--------------------+--------------------+
| or | | |
+--------------------+--------------------+
Non c'è negazione bit a bit in Python (solo l'operatore inverso bit a bit ~
- ma ciò non equivale a not
).
Vedi anche 6.6. Operazioni aritmetiche unarie e bit a bit / binarie e 6.7. Operazioni aritmetiche binarie .
Gli operatori logici (come in molte altre lingue) hanno il vantaggio di essere in cortocircuito. Ciò significa che se il primo operando definisce già il risultato, il secondo operatore non viene valutato affatto.
Per mostrarlo uso una funzione che prende semplicemente un valore, lo stampa e lo restituisce di nuovo. Questo è utile per vedere cosa viene effettivamente valutato a causa delle dichiarazioni di stampa:
>>> def print_and_return(value):
... print(value)
... return value
>>> res = print_and_return(False) and print_and_return(True)
False
Come puoi vedere, viene eseguita una sola istruzione di stampa, quindi Python non ha nemmeno guardato l'operando giusto.
Questo non è il caso degli operatori binari. Questi valutano sempre entrambi gli operandi:
>>> res = print_and_return(False) & print_and_return(True);
False
True
Ma se il primo operando non è sufficiente, ovviamente viene valutato il secondo operatore:
>>> res = print_and_return(True) and print_and_return(False);
True
False
Per riassumere, ecco un'altra tabella:
+-----------------+-------------------------+
| Expression | Right side evaluated? |
+=================+=========================+
| `True` and ... | Yes |
+-----------------+-------------------------+
| `False` and ... | No |
+-----------------+-------------------------+
| `True` or ... | No |
+-----------------+-------------------------+
| `False` or ... | Yes |
+-----------------+-------------------------+
Il True
e False
rappresentano ciò che bool(left-hand-side)
ritorna, non devono essere True
o False
, devono solo tornare True
o False
quando bool
viene chiamato su di loro (1).
Quindi in Pseudo-Code (!) Le funzioni and
e or
funzionano in questo modo:
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)
Si noti che questo è pseudo-codice non codice Python. In Python non è possibile creare funzioni chiamate and
o or
perché queste sono parole chiave. Inoltre non dovresti mai usare "valutare" o if bool(...)
.
Personalizzare il comportamento delle tue classi
Questa implicita bool
chiamata può essere utilizzato per personalizzare come le vostre classi si comportano con and
, or
e not
.
Per mostrare come questo può essere personalizzato, uso questa classe che di nuovo print
è qualcosa per tenere traccia di ciò che sta accadendo:
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)
Quindi vediamo cosa succede con quella classe in combinazione con questi operatori:
>>> 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 non hai un __bool__
metodo, Python verifica anche se l'oggetto ha un __len__
metodo e se restituisce un valore maggiore di zero. Potrebbe essere utile sapere nel caso in cui si crea un contenitore di sequenze.
Vedi anche 4.1. Test del valore di verità .
Array e sottoclassi di NumPy
Probabilmente un po 'oltre lo scopo della domanda originale ma nel caso in cui si tratti di array o sottoclassi di NumPy (come Pandas Series o DataFrames), la bool
chiamata implicita aumenterà il temuto 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().
In questi casi puoi usare la logica e la funzione di NumPy che esegue un elemento and
(o 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 hai a che fare solo con array booleani puoi anche usare gli operatori binari con NumPy, questi eseguono confronti a livello di elementi (ma anche binari):
>>> 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)
Che la bool
chiamata sugli operandi debba tornare True
o che False
non sia completamente corretta. È solo il primo operando che deve restituire un valore booleano nel suo __bool__
metodo:
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)
Questo perché in and
realtà restituisce il primo operando se il primo operando valuta False
e se valuta True
quindi restituisce il secondo operando:
>>> x1
Test(10)
>>> x2
Test(False)
Allo stesso modo per or
il contrario:
>>> Test(True) or Test(10)
Test(True)
>>> Test(False) or Test(10)
Test(10)
Tuttavia, se li si utilizza in if
un'istruzione, if
verrà anche implicitamente richiamato bool
il risultato. Quindi questi punti più fini potrebbero non essere rilevanti per te.