Czy jest jakaś różnica między „foo is None” i „foo == None”?

217

Czy jest jakaś różnica między:

if foo is None: pass

i

if foo == None: pass

Konwencja, którą widziałem w większości kodu Pythona (i kodu, który sam piszę) jest tym pierwszym, ale ostatnio natknąłem się na kod, który używa tego drugiego. Żadna nie jest instancją (i jedyną instancją, IIRC) typu NoneType, więc to nie powinno mieć znaczenia, prawda? Czy są jakieś okoliczności, w których może to być?

Joe Shaw
źródło

Odpowiedzi:

253

iszawsze zwraca, Truejeśli porównuje tę samą instancję obiektu

Natomiast ==ostatecznie określa __eq__()metoda

to znaczy


>>> class Foo(object):
       def __eq__(self, other):
           return True

>>> f = Foo()
>>> f == None
True
>>> f is None
False
Brendan
źródło
51
Możesz dodać, że None nie jest singletonem, więc „None is None” jest zawsze Prawdą.
e-satis,
43
I możesz dodać, że isoperatora nie można dostosować (przeciążony przez klasę zdefiniowaną przez użytkownika).
martineau,
@study Metoda __eq__(self)jest specjalną wbudowaną metodą, która określa sposób ==obsługi w przypadku użycia na obiekcie Python. Tutaj go zastąpiliśmy, aby po ==zastosowaniu na obiektach typu Foozawsze zwracał wartość true. Nie ma równoważnej metody dla isoperatora, więc zachowanie isnie może być zmienione w ten sam sposób.
Brendan
Czy dlatego, że definicja klasy foo nie ma konstruktora, tj. Funkcji init ?
studiuj
49

Możesz przeczytać tę tożsamość i równoważność tego obiektu .

Instrukcja „is” służy do identyfikacji obiektu, sprawdza, czy obiekty odnoszą się do tej samej instancji (ten sam adres w pamięci).

A wyrażenie „==” odnosi się do równości (ta sama wartość).

Borrego
źródło
Hmmm, myślę, że twój link się zmienił, chyba że byłeś zainteresowany tym, jak wywoływać funkcje zewnętrzne z Pythona
Pat
Właśnie próbowałem a=1;b=1;print(a is b) # True. Każdy pomysł, dlaczego a is bokazuje się być prawdziwy, nawet jeśli wydaje się, że są to 2 różne obiekty (inny adres w pamięci)?
Johnny Chiu,
24

Słowo ostrzeżenia:

if foo:
  # do something

To nie dokładnie to samo:

if x is not None:
  # do something

Pierwszy z nich jest testem wartości logicznej i może być oceniany na fałsz w różnych kontekstach. Istnieje wiele rzeczy, które reprezentują fałsz w testach wartości boolowskich, na przykład puste pojemniki, wartości boolowskie. W tej sytuacji żaden również nie ocenia się na fałsz, ale robią to również inne rzeczy.

Tendayi Mawushe
źródło
12

(ob1 is ob2) równy (id(ob1) == id(ob2))

Mykoła Chareczko
źródło
6
... ale (ob jest ob2) jest DUŻO szybszy. Timeit mówi, że „(a to b)” wynosi 0,0365 usec na pętlę, a „(id (a) == id (b))” wynosi 0,153 usec na pętlę. 4.2x szybszy!
AKX
4
iswersja nie wymaga wywołania funkcji, a nie python-interpretatora atrybutu odnośnika w ogóle; tłumacz może natychmiast odpowiedzieć, jeśli ob1 jest w rzeczywistości ob2.
u0b34a0f6ae
17
Nie. {} is {}jest fałszywe i id({}) == id({})może być (i jest w CPython) prawdziwe. Zobacz stackoverflow.com/questions/3877230
Piotr Dobrogost
12

Powodem foo is Nonejest preferowany sposób, w jaki możesz obsługiwać obiekt, który definiuje swój własny __eq__i który definiuje obiekt jako równy Brak. Dlatego zawsze używaj, foo is Nonejeśli chcesz sprawdzić, czy jest to nieaktualne None.

truppo
źródło
8

Nie ma różnicy, ponieważ obiekty, które są identyczne, będą oczywiście równe. Jednak PEP 8 wyraźnie stwierdza, że ​​powinieneś używać is:

Porównania do singletonów, takich jak None, zawsze powinny być wykonywane z, lub nie, nigdy z operatorami równości.

Thijs van Dien
źródło
7

istesty tożsamości, a nie równości. Dla twojej instrukcji foo is nonePython po prostu porównuje adres pamięci obiektów. Oznacza to, że zadajesz pytanie „Czy mam dwie nazwy dla tego samego obiektu?”

==z drugiej strony sprawdza równość określoną __eq__()metodą. Nie dba o tożsamość.

In [102]: x, y, z = 2, 2, 2.0

In [103]: id(x), id(y), id(z)
Out[103]: (38641984, 38641984, 48420880)

In [104]: x is y
Out[104]: True

In [105]: x == y
Out[105]: True

In [106]: x is z
Out[106]: False

In [107]: x == z
Out[107]: True

Nonejest operatorem singletonu. Tak None is Nonejest zawsze.

In [101]: None is None
Out[101]: True
ChillarAnand
źródło
5

W przypadku None nie powinno być różnicy między równością (==) a tożsamością (is). NoneType prawdopodobnie zwraca tożsamość dla równości. Ponieważ None jest jedyną instancją, jaką możesz zrobić z NoneType (myślę, że to prawda), dwie operacje są takie same. W przypadku innych typów nie zawsze tak jest. Na przykład:

list1 = [1, 2, 3]
list2 = [1, 2, 3]
if list1==list2: print "Equal"
if list1 is list2: print "Same"

Spowoduje to wydrukowanie „Równe”, ponieważ listy mają operację porównania, która nie jest domyślnym zwracaniem tożsamości.

Stephen Pellicer
źródło
5

@ Jason :

Polecam użyć czegoś więcej zgodnie z

if foo:
    #foo isn't None
else:
    #foo is None

Nie lubię używać „if foo:”, chyba że foo naprawdę reprezentuje wartość logiczną (tj. 0 lub 1). Jeśli foo jest łańcuchem, obiektem lub czymś innym, „if foo:” może działać, ale dla mnie wygląda to na leniwy skrót. Jeśli sprawdzasz, czy x to Brak, powiedz „jeśli x to Brak:”.

Graeme Perrow
źródło
Sprawdzanie pustych ciągów / list za pomocą „if var” jest preferowanym sposobem. Konwersja boolowska jest dobrze zdefiniowana i zawiera mniej kodu, który nawet działa lepiej. Nie ma powodu, aby to robić „jeśli na przykład len (mylist) == 0”.
truppo
Źle. Załóżmy, że foo = "". Następnie if foozwróci false, a komentarz #foo is Nonejest błędny.
blokeley
Uwaga dla downvoters - moja odpowiedź cytuje odpowiedź, która została usunięta i nie zgadzam się z nią. Jeśli nie podoba ci się kod w mojej odpowiedzi, musisz głosować . :-)
Graeme Perrow,
2

Kilka dodatkowych szczegółów:

  1. isKlauzula faktycznie sprawdza czy oba objects są w tym samym miejscu pamięci, czy nie. tj. czy oba wskazują na to samo miejsce w pamięci i mają to samo id.

  2. W wyniku 1, iszapewnia, czy dwa reprezentowane leksykalnie objects mają identyczne atrybuty (atrybuty atrybutów ...), czy nie

  3. Instancji z podstawowych typów, takich jak bool, int, string(z wyjątkiem kilku) NoneTypeo tę samą wartość będzie zawsze w tym samym miejscu w pamięci.

Na przykład

>>> int(1) is int(1)
True
>>> str("abcd") is str("abcd")
True
>>> bool(1) is bool(2)
True
>>> bool(0) is bool(0)
True
>>> bool(0)
False
>>> bool(1)
True

A ponieważ NoneTypemoże mieć tylko jedno wystąpienie w tabeli „zapytań” Pythona, to pierwsze i drugie są bardziej stylem programisty programisty, który napisał kod (być może dla zachowania spójności), a nie mają subtelnego logicznego powodu wybierz jeden nad drugim.

Krwawiące palce
źródło
2
Wszyscy czytający to: NIE NALEŻY some_string is "bar"porównywać ciągów NIGDY. NIE JEST JEDEN PRZYCZYNA DO AKCEPTACJI, a ZOSTANIE PRZERWA, gdy się tego nie spodziewasz. Fakt, że często działa, jest po prostu dlatego, że CPython wie, że głupio byłoby stworzyć dwa niezmienne obiekty o tej samej treści. Ale to może się jednak zdarzyć.
ThiefMaster
@ ThiefMaster czy odpowiedź ma tendencję do błędnej interpretacji? Choć po przeczytaniu go ponownie I nie mógł znaleźć to być (ze wzmianek o „pewnych wyjątków”). Twoje oświadczenie dotyczy tylko łańcuchów, a nie int, prawda?
Bleeding Fingers
Nie bardzo, ale skoro masz ten przykład w swojej odpowiedzi, pomyślałem, że dobrym pomysłem byłoby ostrzec użytkowników, że to zły pomysł, aby z nich skorzystać. Może dodaj coś w stylu „# cpython specific / not Guaranteed” za tym wierszem ...
ThiefMaster
1

Wniosek Johna Machina, który Nonejest singletonem, jest wnioskiem wzmocnionym przez ten kod.

>>> x = None
>>> y = None
>>> x == y
True
>>> x is y
True
>>> 

Ponieważ Nonejest singletonem x == Nonei x is Nonemiałby ten sam wynik. Jednak moim zdaniem estetycznym x == Nonejest najlepszy.

ncmathsadist
źródło
2
Nie zgadzam się z opinią na końcu tej odpowiedzi. Porównując z żadnymi jawnie, zwykle zamierzone jest, że przedmiotowy obiekt jest dokładnie tym Noneobiektem. Dla porównania, rzadko Falsezdarza się, aby Żaden nie był używany w żadnym innym kontekście, z wyjątkiem tego, że jest podobny do innych wartości, które są prawdziwe. W takich przypadkach bardziej idiomatyczne jest zrobienie czegoś takiegoif x: pass
SingleNegationElimination
0
a is b # returns true if they a and b are true alias
a == b # returns true if they are true alias or they have values that are deemed equivalence 


a = [1,3,4]
b = a[:] #creating copy of list
a is b # if gives false
False
a == b # gives true
True
Aks
źródło