W jaki sposób "and" I "or" działają z wartościami nie-logicznymi?
Próbuję nauczyć się Pythona i natknąłem się na jakiś kod, który jest ładny i krótki, ale nie do końca ma sens
Kontekst był:
def fn(*args):
return len(args) and max(args)-min(args)
Rozumiem, co robi, ale dlaczego python to robi - ie Zwraca wartość zamiast True / False?
10 and 7-2
Zwraca 5. Podobnie, zmiana and na or spowoduje zmianę funkcjonalności. So
10 or 7 - 2
Zwróci 10.
Czy jest to legit / niezawodny styl, czy są na tym jakieś Gotcha?
8 answers
TL; DR
Zaczynamy od podsumowania dwóch zachowań dwóch operatorów logicznych and
i or
. Te idiomy będą stanowić podstawę naszej dyskusji poniżej.
and
Zwraca pierwszą Falsy wartość, jeśli istnieją, w przeciwnym razie zwraca ostatnią wartość w wyrażeniu.
or
Zwraca pierwszą wartość true jeśli istnieje, w przeciwnym razie zwraca ostatnią wartość w wyrażeniu.
Zachowanie jest również streszczone w docs , szczególnie w tej tabeli:
Jedynym operatorem zwracającym wartość logiczną niezależnie od jej operandów jest operator not
.
W 2006 roku został wybrany do Izby Gmin.]}
Oświadczenie
len(args) and max(args) - min(args)
Jest bardzo pythonic zwięzły (i prawdopodobnie mniej czytelny) sposób powiedzenia "Jeśli args
Nie jest pusty, zwróć wynik max(args) - min(args)
", w przeciwnym razie zwróć 0
. Ogólnie, jest to bardziej zwięzła reprezentacja if-else
wyrażenia. Na przykład,
exp1 and exp2
Należy (z grubsza) przetłumaczyć na:
r1 = exp1
if r1:
r1 = exp2
Lub, równoważnie,
r1 = exp2 if exp1 else exp1
Podobnie,
exp1 or exp2
Należy (z grubsza) przetłumaczyć na:
r1 = exp1
if not r1:
r1 = exp2
Lub, równoważnie,
r1 = exp1 if exp1 else exp2
Gdzie exp1
i exp2
są dowolnymi obiektami Pythona lub wyrażeniami zwracającymi jakiś obiekt. Kluczem do zrozumienia zastosowań operatorów logicznych and
i or
jest zrozumienie, że są nie ogranicza się do działania lub zwracania wartości logicznych. Każdy obiekt o wartości prawdziwości może być tutaj przetestowany. Obejmuje to int
, str
, list
, dict
, tuple
, set
, NoneType
, i obiektów zdefiniowanych przez użytkownika. Nadal obowiązują zasady zwarcia.
Ale co to jest prawdziwość?
Odnosi się do sposobu, w jaki obiekty są oceniane, gdy są używane w wyrażeniach warunkowych. @ Patrick Haugh ładnie podsumowuje prawdziwość w ten post.
Wszystkie wartości są uważane za "prawdziwe", z wyjątkiem następujących, które są "falsy": {]}
None
False
0
0.0
0j
Decimal(0)
Fraction(0, 1)
[]
- pustylist
{}
- pustydict
()
- pustytuple
''
- pustystr
b''
- pustybytes
set()
- pustyset
- pusty
range
, jakrange(0)
- obiekty, dla których
obj.__bool__()
zwracaFalse
obj.__len__()
zwraca0
Wartość "prawdziwości" spełni kontrolę wykonaną przez
if
lubwhile
Oświadczenia. Używamy "prawdziwości" i "fałszu", aby odróżnić odbool
wartościTrue
iFalse
.
Jak and
Działa
Opieramy się na pytaniu OP jako segue do dyskusji na temat tego, jak te operatory w tych przypadki.
Dana funkcja z definicją
def foo(*args): ...
Jak zwrócić różnicę między wartością minimalną i maksymalną na liście zero czy więcej argumentów?
Znalezienie minimum i maksimum jest łatwe (użyj wbudowanych funkcji!). Jedynym problemem jest odpowiednia obsługa przypadku narożnego, w którym lista argumentów może być pusta(na przykład wywołanie foo()
). Możemy zrobić oba w jednej linii dzięki and
operator:
def foo(*args):
return len(args) and max(args) - min(args)
foo(1, 2, 3, 4, 5)
# 4
foo()
# 0
Ponieważ używane jest and
, drugie wyrażenie musi być również ocenione, jeśli pierwsze to True
. Zauważ, że jeśli pierwsze wyrażenie jest obliczone jako prawdziwe, zwracaną wartością jest zawsze wynik drugiego wyrażenia. Jeżeli pierwsze wyrażenie jest obliczone jako Falsy, wtedy zwracany wynik jest wynikiem pierwszego wyrażenia.
W powyższej funkcji, jeśli foo
otrzyma jeden lub więcej argumentów, len(args)
jest większy niż 0
(liczba dodatnia), więc zwracany wynik to max(args) - min(args)
. OTOH, jeśli nie zostaną przekazane żadne argumenty, len(args)
jest 0
, co jest Falsy, a 0
jest zwracane.
Zauważ, że alternatywnym sposobem zapisu tej funkcji byłoby:
def foo(*args):
if not len(args):
return 0
return max(args) - min(args)
Lub, bardziej zwięźle,
def foo(*args):
return 0 if not args else max(args) - min(args)
Jeśli oczywiście, żadna z tych funkcji nie wykonuje żadnego sprawdzania typu, więc jeśli nie ufasz całkowicie dostarczonemu wejściu, nie polegaj na prostocie tych konstrukcji.
Jak or
Prace
Wyjaśniam działanie or
w podobny sposób na wymyślonym przykładzie.
Dana funkcja z definicją
def foo(*args): ...
Jak wypełnisz
foo
aby zwrócić wszystkie liczby nad9000
?
Używamy or
do obsługi narożnej walizki. Definiujemy foo
jako:
def foo(*args):
return [x for x in args if x > 9000] or 'No number over 9000!'
foo(9004, 1, 2, 500)
# [9004]
foo(1, 2, 3, 4)
# 'No number over 9000!'
foo
wykonuje filtrację na liście, aby zachować wszystkie liczby nad 9000
. Jeśli istnieją takie liczby, wynikiem zestawienia jest niepustaĺ ' a lista, ktĂłra jest prawdÄ..., wiÄ ™ c jest zwracana (zwarcie w akcji tutaj). Jeśli nie ma takich liczb, to wynikiem comp listy jest []
, który jest Falsy. Tak więc drugie wyrażenie jest teraz obliczane (niepusty łańcuch) i jest zwracane.
Używając conditionals, możemy zapisać tę funkcję jako,
def foo(*args):
r = [x for x in args if x > 9000]
if not r:
return 'No number over 9000!'
return r
Tak jak poprzednio, struktura ta jest bardziej elastyczna pod względem obsługi błędów.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-20 17:17:20
Cytowanie z Python Docs
Zauważ, że ani
and
anior
ogranicz wartość i typ zwracają doFalse
iTrue
, ale raczej zwracają ostatni oceniany argument. To czasami jest przydatna, np. Jeśli {[6] } jest ciągiem znaków, który powinien być zastąpiony przez wartość domyślna jeśli jest pusta, wyrażenies or 'foo'
daje pożądana wartość.
Tak więc Python został zaprojektowany do oceny wyrażenia logiczne i powyższa dokumentacja dają nam wgląd w to, dlaczego tak się stało.
Aby uzyskać wartość logiczną wystarczy ją wpisać.
return bool(len(args) and max(args)-min(args))
Dlaczego?
Zwarcie.
Na przykład:
2 and 3 # Returns 3 because 2 is Truthy so it has to check 3 too
0 and 3 # Returns 0 because 0 is Falsey and there's no need to check 3 at all
To samo dotyczy or
również, to znaczy zwróci wyrażenie, które jest prawdziwe, gdy tylko je znajdzie, ponieważ ocena reszty wyrażenia jest zbędna.
Zamiast zwracać hardcore True
lub False
, Python zwraca prawdziwe lub fałszywe , które i tak będą oceniać na True
lub False
. Możesz użyć wyrażenia tak, jak jest, i nadal będzie działać.
Aby wiedzieć, co jest prawdziwe i fałszywe , sprawdź odpowiedź Patricka Haugha
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-30 05:41:43
I i lub wykonują logikę logiczną, ale zwracają jedną z rzeczywistych wartości podczas porównywania. Podczas używania i wartości są oceniane W kontekście logicznym od lewej do prawej. 0, ", [], (), {}, i żadne {[7] } nie są fałszywe w kontekście logicznym; Wszystko inne jest prawdą.
Jeśli wszystkie wartości są prawdziwe w kontekście logicznym, i zwraca ostatnią wartość.
>>> 2 and 5
5
>>> 2 and 5 and 10
10
Jeśli jakakolwiek wartość jest false w kontekście logicznym i zwraca pierwsza wartość false.
>>> '' and 5
''
>>> 2 and 0 and 5
0
Więc kod
return len(args) and max(args)-min(args)
Zwraca wartość max(args)-min(args)
gdy istnieje args , w przeciwnym wypadku zwraca len(args)
, która wynosi 0.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-30 03:55:20
Czy jest to legit / niezawodny styl, czy są na tym jakieś Gotcha?
To jest legit, jest to ocena zwarcia , gdzie zwracana jest ostatnia wartość.
Dajesz dobry przykład. Funkcja zwróci0
, Jeśli żadne argumenty nie zostaną przekazane, a kod nie będzie musiał sprawdzać, czy nie podano specjalnego przypadku żadnych argumentów.
Innym sposobem użycia tego, jest domyślenie argumentów None na zmienny prymityw, jak pusta lista:
def fn(alist=None):
alist = alist or []
....
Jeśli niektóre nieprawdziwe wartości są przekazywane do alist
to domyślnie pusta lista, poręczny sposób na uniknięcie instrukcji if
i zmiennego domyślnego argumentu pitfall
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-30 03:26:57
Gotchas
Tak, jest kilku gotchów.fn() == fn(3) == fn(4, 4)
Po pierwsze, Jeśli fn
zwróci 0
, nie możesz wiedzieć, czy została wywołana bez żadnego parametru, z jednym parametrem lub z wieloma, równymi parametrami:
>>> fn()
0
>>> fn(3)
0
>>> fn(3, 3, 3)
0
Co oznacza fn
?
Python jest więc językiem dynamicznym. Nie jest nigdzie określone, co fn
robi, jakie ma być jego wejście i jak ma wyglądać jego wyjście. Dlatego bardzo ważne jest, aby poprawnie nazwać funkcję. Podobnie, argumenty nie muszą być wywoływane args
. delta(*numbers)
lub calculate_range(*numbers)
może lepiej opisać, co funkcja ma robić.
Błędy argumentów
Wreszcie, operator logiczny and
ma zapobiegać niepowodzeniu funkcji, jeśli zostanie wywołana bez żadnego argumentu. Mimo to nie działa, jeśli jakiś argument nie jest liczbą:
>>> fn('1')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'
>>> fn(1, '2')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in fn
TypeError: '>' not supported between instances of 'str' and 'int'
>>> fn('a', 'b')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in fn
TypeError: unsupported operand type(s) for -: 'str' and 'str'
Możliwa alternatywa
Oto sposób na napisanie funkcji zgodnie z " łatwiej prosić o przebaczenie niż pozwolenie."zasada :
def delta(*numbers):
try:
return max(numbers) - min(numbers)
except TypeError:
raise ValueError("delta should only be called with numerical arguments") from None
except ValueError:
raise ValueError("delta should be called with at least one numerical argument") from None
Jako przykład:
>>> delta()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in delta
ValueError: delta should be called with at least one numerical argument
>>> delta(3)
0
>>> delta('a')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 'b')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta('a', 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in delta
ValueError: delta should only be called with numerical arguments
>>> delta(3, 4.5)
1.5
>>> delta(3, 5, 7, 2)
5
Jeśli naprawdę nie chcesz podnosić wyjątku, gdy delta
jest wywoływane bez żadnego argumentu, możesz zwrócić jakąś wartość, która nie może być możliwa w inny sposób (np. -1
lubNone
):
>>> def delta(*numbers):
... try:
... return max(numbers) - min(numbers)
... except TypeError:
... raise ValueError("delta should only be called with numerical arguments") from None
... except ValueError:
... return -1 # or None
...
>>>
>>> delta()
-1
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-30 15:12:52
Czy jest to legit / niezawodny styl, czy są na tym jakieś Gotcha?
Chciałbym dodać do tego pytania, że jest nie tylko legalny i niezawodny, ale także bardzo praktyczny. Oto prosty przykład:
>>>example_list = []
>>>print example_list or 'empty list'
empty list
Dlatego naprawdę możesz go używać na swoją korzyść. Aby być poborcą tak to widzę:
Or
operator
Operator Pythona or
zwraca pierwszą wartość Truth-y lub ostatnią wartość i przystanki
And
operator
Operator Pythona and
zwraca pierwszą wartość False-y lub ostatnią i zatrzymuje
Za kulisami
W Pythonie wszystkie liczby są interpretowane jako True
z wyjątkiem 0. Dlatego powiedzenie:
0 and 10
Jest tym samym co:
False and True
Czyli wyraźnie False
. Jest więc logiczne, że zwraca 0
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-30 03:39:25
Tak. Jest to prawidłowe zachowanie i porównanie.
Przynajmniej w Pythonie, A and B
zwraca B
Jeśli A
jest zasadniczo True
w tym Jeśli A
nie jest Null, nie None
nie jest pustym kontenerem (takim jak pusty list
, dict
, itp.). A
jest zwracane IFF A
jest zasadniczo False
lub None
lub Empty lub Null.
Z drugiej strony, A or B
zwraca A
Jeśli A
jest zasadniczo True
w tym Jeśli A
nie jest Null, nie None
nie jest pustym kontenerem (takim jak pusty list
, dict
, etc), w przeciwnym razie zwraca B
.
Łatwo nie zauważyć (lub przeoczyć) tego zachowania, ponieważ w Pythonie każdy niepusty obiekt, który ewaluuje do True, jest traktowany jak boolean.
Na przykład, wszystkie poniższe będą drukować "True"
if [102]:
print "True"
else:
print "False"
if "anything that is not empty or None":
print "True"
else:
print "False"
if {1, 2, 3}:
print "True"
else:
print "False"
Z drugiej strony, wszystkie poniższe będą drukować "False"
if []:
print "True"
else:
print "False"
if "":
print "True"
else:
print "False"
if set ([]):
print "True"
else:
print "False"
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-31 05:46:01
Aby zrozumieć w prosty sposób,
Oraz : if first_val is False return first_val else second_value
Eg:
1 and 2 # here it will return 2 because 1 is not False
Ale,
0 and 2 # will return 0 because first value is 0 i.e False
I = > jeśli ktoś jest fałszywy, to będzie fałszywy. jeśli oba są prawdziwe, to tylko to stanie się prawdą
Lub : if first_val is False return second_val else first_value
Powód jest taki, że jeśli pierwsza jest fałszywa, to sprawdź, czy 2 jest prawdziwe, czy nie.
Eg:
1 or 2 # here it will return 1 because 1 is not False
Ale,
0 or 2 # will return 2 because first value is 0 i.e False
Or = > jeśli ktoś fałszywy, to będzie prawdziwy. więc jeśli pierwsza wartość jest false bez względu na to, co 2 wartość Załóżmy być. więc zwraca drugą wartość, jaką kiedykolwiek może być.
Jeśli ktoś jest prawdą, to stanie się prawdą. jeśli oba są fałszywe, to stanie się fałszywe.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-06-19 06:44:40