Dostaję błąd w warunkowym IF. Co ja robię źle?
Istnieje powód, dla którego dostajesz SyntaxError
to, że &&
w Pythonie nie ma operatora. Podobnie ||
i nie!
są poprawnymi operatorami języka Python.
Niektóre operatory, które możesz znać z innych języków, mają w Pythonie inną nazwę. Operatory logiczne &&
i ||
faktycznie są nazywane and
i or
. Podobnie !
wywoływany jest operator logicznej negacji not
.
Więc możesz po prostu napisać:
if len(a) % 2 == 0 and len(b) % 2 == 0:
lub nawet:
if not (len(a) % 2 or len(b) % 2):
Kilka dodatkowych informacji (które mogą się przydać):
Podsumowałem operatora „ekwiwalenty” w tej tabeli:
+------------------------------+---------------------+
| Operator (other languages) | Operator (Python) |
+==============================+=====================+
| && | and |
+------------------------------+---------------------+
| || | or |
+------------------------------+---------------------+
| ! | not |
+------------------------------+---------------------+
Zobacz także dokumentację Pythona: 6.11. Operacje logiczne .
Oprócz operatorów logicznych Python ma także operatory bitowe / binarne:
+--------------------+--------------------+
| Logical operator | Bitwise operator |
+====================+====================+
| and | & |
+--------------------+--------------------+
| or | | |
+--------------------+--------------------+
W Pythonie nie ma bitowej negacji (tylko bitowy operator odwrotny ~
- ale to nie jest równoważne not
).
Zobacz także 6.6. Jednoargumentowe operacje arytmetyczne i bitowe / binarne oraz 6.7. Binarne operacje arytmetyczne .
Operatory logiczne (podobnie jak w wielu innych językach) mają tę zaletę, że są zwarte. Oznacza to, że jeśli pierwszy argument już określa wynik, to drugi operator nie jest w ogóle oceniany.
Aby to pokazać, używam funkcji, która po prostu przyjmuje wartość, drukuje ją i zwraca ponownie. Jest to przydatne, aby zobaczyć, co jest faktycznie oceniane na podstawie instrukcji print:
>>> def print_and_return(value):
... print(value)
... return value
>>> res = print_and_return(False) and print_and_return(True)
False
Jak widać, wykonywana jest tylko jedna instrukcja print, więc Python tak naprawdę nawet nie spojrzał na odpowiedni operand.
Nie dotyczy to operatorów binarnych. Zawsze oceniają oba operandy:
>>> res = print_and_return(False) & print_and_return(True);
False
True
Ale jeśli pierwszy operand nie wystarczy, wówczas oceniany jest drugi operator:
>>> res = print_and_return(True) and print_and_return(False);
True
False
Podsumowując, oto kolejna tabela:
+-----------------+-------------------------+
| Expression | Right side evaluated? |
+=================+=========================+
| `True` and ... | Yes |
+-----------------+-------------------------+
| `False` and ... | No |
+-----------------+-------------------------+
| `True` or ... | No |
+-----------------+-------------------------+
| `False` or ... | Yes |
+-----------------+-------------------------+
True
I False
reprezentują co bool(left-hand-side)
powraca, nie trzeba być True
albo False
, po prostu trzeba zwrócić True
lub False
gdy bool
nazywa się na nich (1).
Tak więc w Pseudokodzie (!) Funkcje and
i or
działają w następujący sposób:
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)
Zauważ, że to pseudo-kod, a nie kod Pythona. W Pythonie nie można tworzyć wywoływanych funkcji and
lub or
ponieważ są to słowa kluczowe. Nigdy też nie powinieneś używać „oceny” lub if bool(...)
.
Dostosowywanie zachowania własnych klas
To niejawne bool
wywołanie może być użyte do dostosowania sposobu, w jaki zachowują się twoje klasy and
, or
oraz not
.
Aby pokazać, jak można to dostosować, używam tej klasy, która ponownie print
jest czymś, co pozwala śledzić, co się dzieje:
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)
Zobaczmy więc, co dzieje się z tą klasą w połączeniu z tymi operatorami:
>>> 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)
Jeśli nie masz __bool__
metody, Python sprawdza również, czy obiekt ma __len__
metodę i czy zwraca wartość większą niż zero. Może to być przydatne, jeśli utworzysz kontener sekwencji.
Zobacz także 4.1. Testowanie wartości prawdy .
Tablice i podklasy NumPy
Prawdopodobnie nieco poza zakres pierwotnego pytania, ale w przypadku, gdy masz do czynienia z tablicami lub podklasami NumPy (takimi jak Pandas Series lub DataFrames), niejawne bool
wywołanie wzbudzi przerażenie 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().
W takich przypadkach można użyć funkcji logicznej i funkcji NumPy, która wykonuje element and
(lub 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])
Jeśli masz do czynienia tylko z tablicami logicznymi, możesz także użyć operatorów binarnych z NumPy, które wykonują porównania elementarne (ale także binarne):
>>> 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)
Że bool
wywołanie na operandach musi zostać zwrócone True
lub False
nie jest całkowicie poprawne. To tylko pierwszy operand, który musi zwrócić wartość logiczną w swojej __bool__
metodzie:
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)
Jest tak, ponieważ and
faktycznie zwraca pierwszy operand, jeśli pierwszy operand ewaluuje False
i jeśli ewaluuje, True
to zwraca drugi operand:
>>> x1
Test(10)
>>> x2
Test(False)
Podobnie, or
ale tylko na odwrót:
>>> Test(True) or Test(10)
Test(True)
>>> Test(False) or Test(10)
Test(10)
Jeśli jednak użyjesz ich w if
instrukcji if
, domyślnie wywoła również bool
wynik. Więc te drobniejsze punkty mogą nie być dla ciebie odpowiednie.