Dlaczego „nie (prawda) w [False, True]” zwraca False?

483

Jeśli to zrobię:

>>> False in [False, True]
True

To powraca True. Po prostu dlatego, że Falsejest na liście.

Ale jeśli to zrobię:

>>> not(True) in [False, True]
False

To powraca False. Podczas gdy not(True)jest równy False:

>>> not(True)
False

Dlaczego?

Texom512
źródło
2
twoje nawiasy są mylącenot(True) in [False, True]
Grijesh Chauhan

Odpowiedzi:

730

Operator pierwszeństwo 2.x , 3.x . Pierwszeństwo notjest niższe niż in. Jest to więc równoważne z:

>>> not ((True) in [False, True])
False

To jest to, czego chcesz:

>>> (not True) in [False, True]
True

Jak zauważa @Ben: Zaleca się, aby nigdy nie pisać not(True), preferuj not True. Ten pierwszy sprawia, że ​​wygląda jak wywołanie funkcji, podczas gdy notjest operatorem, a nie funkcją.

Yu Hao
źródło
279
@ Texom512: Polecam także nigdy nie pisać not(True); wolę not True. Pierwszy sprawia, że ​​wygląda jak wywołanie funkcji, stąd pochodzi twoje zamieszanie; gdyby notbyła funkcją, to not(True) in ...nie mogłaby być not ((True) in ...). Musisz wiedzieć, że jest to operator (lub znajdziesz się w takich sytuacjach), więc powinieneś napisać go jak operator, a nie ukrywać go jako funkcję.
Ben
7
Ponadto, jeśli zamierzasz używać odstępów w celu wskazania pierwszeństwa z korzyścią dla czytelnika, najpierw upewnij się, że masz rację. Prawdopodobnie pisanie a + b*c + djest w porządku, pisanie jest bardzo złe a+b * c+d. Tak samo not(True)źle jest z tego powodu.
Steve Jessop,
32
Właściwie nigdy nie pisz not True. Napisz Falsezamiast tego.
Darkhogg
10
Przypuszczalnie w prawdziwym życiu nie będzie pisać not True, można by pisać coś takiego not myfunc(x,y,z), gdzie myfuncjest jakaś funkcja, która zwraca Truelub False.
Nate CK,
3
@ BenC.R.Leggiero Tak postąpiłem w pierwotnej odpowiedzi , a inni to poprawili. Obecna wersja jest dla mnie wystarczająco jasna, nie sądzę, że trudno jest zrozumieć bez zbędnych nawiasów, ponieważ wskazano kluczowy problem, zrozumienie reszty jest podstawową umiejętnością programisty.
Yu Hao,
76

not x in y jest oceniany jako x not in y

Możesz dokładnie zobaczyć, co się dzieje, dezasemblując kod. Pierwszy przypadek działa zgodnie z oczekiwaniami:

>>> x = lambda: False in [False, True]
>>> dis.dis(x)
  1           0 LOAD_GLOBAL              0 (False)
              3 LOAD_GLOBAL              0 (False)
              6 LOAD_GLOBAL              1 (True)
              9 BUILD_LIST               2
             12 COMPARE_OP               6 (in)
             15 RETURN_VALUE

Drugi przypadek ocenia na True not in [False, True], co Falsewyraźnie:

>>> x = lambda: not(True) in [False, True]
>>> dis.dis(x)
  1           0 LOAD_GLOBAL              0 (True)
              3 LOAD_GLOBAL              1 (False)
              6 LOAD_GLOBAL              0 (True)
              9 BUILD_LIST               2
             12 COMPARE_OP               7 (not in)
             15 RETURN_VALUE        
>>> 

Zamiast tego chciałeś wyrazić to (not(True)) in [False, True], co zgodnie z oczekiwaniami True, i możesz zobaczyć, dlaczego:

>>> x = lambda: (not(True)) in [False, True]
>>> dis.dis(x)
  1           0 LOAD_GLOBAL              0 (True)
              3 UNARY_NOT           
              4 LOAD_GLOBAL              1 (False)
              7 LOAD_GLOBAL              0 (True)
             10 BUILD_LIST               2
             13 COMPARE_OP               6 (in)
             16 RETURN_VALUE        
Roshan Mathews
źródło
13
Zawsze jest facet, disale jest to bardzo cenna odpowiedź, ponieważ pokazuje, że faktycznie not injest używana
jamylak
21
Kod bajtowy jest szczegółem implementacji interpretera CPython. To jest odpowiedź CPython na pytanie w Pythonie, w rzeczywistości można na nie lepiej odpowiedzieć bezpośrednio z odwołania do języka.
wim
5
@ wim Argumentowałbym, że implementacja kodu bajtowego nie jest tak ważna jak faktyczny demontaż. Inne implementacje gwarantują wygenerowanie czegoś funkcjonalnie identycznego, więc zrozumienie jednego demontażu oferuje wystarczająco dużo wglądu, aby zrozumieć „dlaczego”, a nie niski poziom „jak”.
Alex Pana
36

Pierwszeństwo operatora. inwiąże się mocniej niż not, więc twoje wyrażenie jest równoważne not((True) in [False, True]).

mooiamaduck
źródło
33

Chodzi o pierwszeństwo operatora ( injest silniejsze niż not). Ale można to łatwo poprawić, dodając nawiasy we właściwym miejscu:

(not(True)) in [False, True]  # prints true

pisanie:

not(True) in [False, True]

jest taki sam jak:

not((True) in [False, True])

który sprawdza, czy Trueznajduje się na liście i zwraca „nie” wyniku.

alfasin
źródło
14

Ocenia się jako not True in [False, True], który zwraca, Falseponieważ Truejest w[False, True]

Jeśli spróbujesz

>>>(not(True)) in [False, True]
True

Otrzymujesz oczekiwany wynik.

użytkownik3636636
źródło
13

Wraz z innymi odpowiedziami, w których wspomniano o pierwszeństwie, notjest niższy niż in, w rzeczywistości twoje stwierdzenie jest równoważne z:

not (True in [False, True])

Ale zauważ, że jeśli nie oddzielisz swojego warunku od innych, Python użyje 2 ról ( precedencelub chaining) w celu ich oddzielenia, aw tym przypadku python użył pierwszeństwa. Pamiętaj również, że jeśli chcesz rozdzielić warunek, musisz umieścić wszystkie warunki w nawiasach, a nie tylko obiekt lub wartość:

(not True) in [False, True]

Ale jak wspomniano, istnieje kolejna modyfikacja Pythona w łańcuchach operatorów :

Na podstawie dokumentacji Pythona :

Należy pamiętać, że porównania, testy członkostwa i testy tożsamości mają ten sam priorytet i mają funkcję łączenia od lewej do prawej, jak opisano w sekcji Porównania.

Na przykład wynikiem następującej instrukcji jest False:

>>> True == False in [False, True]
False

Ponieważ python będzie łączyć łańcuchy instrukcji w następujący sposób:

(True == False) and (False in [False, True])

Co to dokładnie False and Truejest False.

Możesz założyć, że centralny obiekt będzie współdzielony między 2 operacjami i innymi obiektami (w tym przypadku False).

I zauważ, że dotyczy to również wszystkich porównań, w tym testów członkostwa i operacji testów tożsamości, które są następującymi operandami:

in, not in, is, is not, <, <=, >, >=, !=, ==

Przykład:

>>> 1 in [1,2] == True
False

Innym znanym przykładem jest zakres liczb:

7<x<20

co jest równe:

7<x and x<20   
Kasramvd
źródło
6

Zobaczmy to jako operację sprawdzania zamknięcia kolekcji: [False, True]to lista zawierająca niektóre elementy.

True in [False, True]Zwraca wyrażenie True, podobnie jak Trueelement zawarty na liście.

Dlatego not True in [False, True]daje „logiczne przeciwieństwo”, notwynik powyższego wyrażenia (bez nawiasów dla zachowania pierwszeństwa, ponieważ inma większy priorytet niż notoperator). Dlatego not Truenastąpi False.

Z drugiej strony, (not True) in [False, True]jest równy False in [False, True], który jest True( Falsejest zawarty na liście).

Nick Louloudakis
źródło
6

Aby wyjaśnić niektóre inne odpowiedzi, dodawanie nawiasów po jednoargumentowym operatorze nie zmienia jego pierwszeństwa. not(True)nie notwiąże się ściślej True. To tylko zbędny zestaw nawiasów wokół True. To jest tak samo jak (True) in [True, False]. Nawiasy nic nie robią. Jeśli chcesz, aby wiązanie było ściślejsze, musisz umieścić nawiasy wokół całego wyrażenia, co oznacza zarówno operatora, jak i operand, tj (not True) in [True, False].

Aby zobaczyć to z innej perspektywy, zastanów się

>>> -2**2
-4

**wiąże się ściślej niż -, dlatego otrzymujesz ujemny wynik dwóch kwadratów, a nie kwadrat ujemnych dwóch (co byłoby dodatnimi czterema).

Co jeśli chcesz kwadrat dwóch ujemnych? Oczywiście dodajemy nawiasy:

>>> (-2)**2
4

Nie można jednak oczekiwać, że podadzą następujące informacje 4

>>> -(2)**2
-4

ponieważ -(2)jest taki sam jak -2. Nawiasy absolutnie nic nie robią. not(True)jest dokładnie taki sam.

asmeurer
źródło