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?

Author: John Kugelman, 2017-10-30

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:

Tutaj wpisz opis obrazka

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)
  • [] - pusty list
  • {} - pusty dict
  • () - pusty tuple
  • '' - pusty str
  • b'' - pusty bytes
  • set() - pusty set
  • pusty range, jak range(0)
  • obiekty, dla których
    • obj.__bool__() zwraca False
    • obj.__len__() zwraca 0

Wartość "prawdziwości" spełni kontrolę wykonaną przez if lub while Oświadczenia. Używamy "prawdziwości" i "fałszu", aby odróżnić od bool wartości True i False.


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 nad 9000?

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.

 150
Author: cs95,
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 ani or ogranicz wartość i typ zwracają do False i True, 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żenie s 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

 19
Author: Amit Joki,
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.

 7
Author: Nithin Varghese,
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óci 0, 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 ifi zmiennego domyślnego argumentu pitfall

 5
Author: salparadise,
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
 3
Author: Eric Duminil,
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

 0
Author: scharette,
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"
 0
Author: emmanuelsa,
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.
 0
Author: Mohideen bin Mohammed,
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