Zrozumienie operatora „is” w Pythonie

110

isOperator nie odpowiada wartości zmiennych, ale same instancje.

Co to naprawdę oznacza?

Zadeklarowałem dwie zmienne o nazwach xi yprzypisaniu tych samych wartości w obu zmiennych, ale zwraca to fałsz, gdy używam isoperatora.

Potrzebuję wyjaśnienia. Oto mój kod.

x = [1, 2, 3]
y = [1, 2, 3]

print(x is y)  # It prints false!
aniskhan001
źródło
Powiązane pytanie stackoverflow.com/questions/38189660/…
Kasravnd

Odpowiedzi:

181

Źle zrozumiałeś, co istestuje operator. Sprawdza, czy dwie zmienne wskazują ten sam obiekt , a nie, czy dwie zmienne mają tę samą wartość.

Z dokumentacji dla isoperatora :

Operatory isi is nottest tożsamości obiektu: x is yjest prawdziwe wtedy i tylko wtedy, gdy xi ysą tym samym obiektem.

==Zamiast tego użyj operatora:

print(x == y)

To drukuje True. xi ysą to dwie osobne listy:

x[0] = 4
print(y)  # prints [1, 2, 3]
print(x == y)   # prints False

Jeśli użyjesz id()funkcji , zobaczysz to xi ymasz różne identyfikatory:

>>> id(x)
4401064560
>>> id(y)
4401098192

ale jeśli miałbyś przypisać ydo, xto oba wskazują ten sam obiekt:

>>> x = y
>>> id(x)
4401064560
>>> id(y)
4401064560
>>> x is y
True

i ispokazuje, że oba są tym samym obiektem, zwraca True.

Pamiętaj, że w Pythonie nazwy to tylko etykiety odwołujące się do wartości ; możesz mieć wiele nazw wskazujących na ten sam obiekt. isinformuje, czy dwie nazwy wskazują na jeden i ten sam obiekt. ==informuje, czy dwie nazwy odnoszą się do obiektów o tej samej wartości.

Martijn Pieters
źródło
13
Więc A is Bto to samo co id(A) == id(B).
imallett
2
@imallett: to proxy dla tego samego testu, pod warunkiem, że nie przechowujesz id(A)w zmiennej i spodziewasz variable == id(B)się, że później nadal będzie działać; jeśli Azostał w międzyczasie usunięty, Bmógł otrzymać to samo miejsce w pamięci.
Martijn Pieters
1
Nie udało się sformatować w komentarzach. Ale jest interesująca rzecz. :) >>> x = 5 \n>>> y = 5 \n>>> x jest y \nPrawda \n>>> x == y \nPrawda \n>>>\n
Haranadh
5
Małe liczby całkowite są internowane w CPythonie, ponieważ są używane tak często. To optymalizacja. x = 5; y = 5; x jest y => True, ponieważ id (x) == id (y). Jest to ten sam obiekt będący liczbą całkowitą, który jest ponownie używany. Działa w Pythonie, ponieważ liczby całkowite są niezmienne. Jeśli zrobisz x = 1.0; y = 1,0 lub x = 9999; y = 9999, to nie będzie ta sama tożsamość, ponieważ zmiennoprzecinkowe i większe liczby int nie są internowane.
Magnus Lyckå
1
@ MagnusLyckå istnieją inne optymalizacje, które mogą powodować buforowanie niezmiennych obiektów. Na przykład, jeśli uruchomisz przykład w nowej funkcji lub razem z oddzielającym średnikiem w interaktywnym interpretatorze, zobaczysz, że również mają ten sam identyfikator.
Martijn Pieters
60

Inny duplikat pytał, dlaczego dwa równe ciągi generalnie nie są identyczne, na co tak naprawdę nie ma odpowiedzi:

>>> x = 'a' 
>>> x += 'bc'
>>> y = 'abc'
>>> x == y
True
>>> x is y
False

Więc dlaczego nie są tym samym ciągiem? Szczególnie biorąc pod uwagę to:

>>> z = 'abc'
>>> w = 'abc'
>>> z is w
True

Odłóżmy na chwilę drugą część. Jak pierwszy mógł być prawdziwy?

Interpreter musiałby mieć „tablicę interningową”, tabelę odwzorowującą wartości łańcuchowe na obiekty łańcuchowe, więc za każdym razem, gdy próbujesz utworzyć nowy łańcuch z zawartością 'abc', otrzymujesz ten sam obiekt. Wikipedia zawiera bardziej szczegółowe omówienie tego, jak działa staż.

A Python ma tablicę umieszczania łańcuchów; możesz ręcznie internować ciągi za pomocą sys.internmetody.

W rzeczywistości Python może automatycznie internować wszystkie niezmienne typy, ale nie jest to wymagane . Różne implementacje będą miały różne wartości.

CPython (implementacja, której używasz, jeśli nie wiesz, której implementacji używasz) auto-interns małe liczby całkowite i niektóre specjalne single, takie jak False, ale nie łańcuchy (lub duże liczby całkowite, małe krotki lub cokolwiek innego). Możesz to dość łatwo zobaczyć:

>>> a = 0
>>> a += 1
>>> b = 1
>>> a is b
True
>>> a = False
>>> a = not a
>>> b = True
a is b
True
>>> a = 1000
>>> a += 1
>>> b = 1001
>>> a is b
False

OK, ale dlaczego były zi widentyczne?

To nie jest interpreter automatycznie internujący, to wartości składane kompilatora.

Jeżeli ten sam ciąg czasu kompilacji pojawia się dwa razy w tym samym module (co dokładnie oznacza to jest trudne do zdefiniowania, to nie to samo, co ciągiem znaków, ponieważ r'abc', 'abc'i 'a' 'b' 'c'są różne literały ale ten sam ciąg, ale łatwe do zrozumienia intuicyjnie), kompilator utworzy tylko jedno wystąpienie ciągu z dwoma odwołaniami.

W rzeczywistości kompilator może pójść nawet dalej: 'ab' + 'c'może zostać przekonwertowany 'abc'przez optymalizator, w którym to przypadku może zostać złożony razem ze 'abc'stałą w tym samym module.

Ponownie, jest to coś, co Python jest dozwolony, ale nie jest do tego zobowiązany. Ale w tym przypadku CPython zawsze składa małe łańcuchy (a także np. Małe krotki). (Chociaż kompilator instrukcji interpretera interaktywnego nie uruchamia takiej samej optymalizacji, jak kompilator modułu w czasie, więc nie zobaczysz dokładnie tych samych wyników interaktywnie).


Co więc powinieneś z tym zrobić jako programista?

Jak nic. Prawie nigdy nie masz powodu, by przejmować się, czy dwie niezmienne wartości są identyczne. Jeśli chcesz wiedzieć, kiedy możesz użyć a is bzamiast a == b, zadajesz niewłaściwe pytanie. Po prostu zawsze używaj a == bz wyjątkiem dwóch przypadków:

  • Aby uzyskać bardziej czytelne porównania z wartościami pojedynczymi, takimi jak x is None.
  • W przypadku wartości zmiennych, kiedy musisz wiedzieć, czy mutacja xwpłynie na y.
abarnert
źródło
1
Doskonałe wyjaśnienie, zwłaszcza twoja rada na końcu.
DavidG
Dziękuję za szczegółowe wyjaśnienie. Czy ktoś wie: jeśli wi zsą identyczne ze względu na składane wartości kompilatora, dlaczego działa to również w REPL, nawet używając id()do sprawdzenia referencji? Korzystanie z REPL w Pythonie 3.7
Chi-chi
8

iszwraca prawdę tylko wtedy, gdy są faktycznie tym samym obiektem. Gdyby były takie same, zmiana na jedną pojawiłaby się również w drugiej. Oto przykład różnicy.

>>> x = [1, 2, 3]
>>> y = [1, 2, 3]
>>> print x is y
False
>>> z = y
>>> print y is z
True
>>> print x is z
False
>>> y[0] = 5
>>> print z
[5, 2, 3]
Mark Okup
źródło
8

Gdy pojawi się zduplikowane pytanie , analogia może zadziałać:

# - Darling, I want some pudding!
# - There is some in the fridge.

pudding_to_eat = fridge_pudding
pudding_to_eat is fridge_pudding
# => True

# - Honey, what's with all the dirty dishes?
# - I wanted to eat pudding so I made some. Sorry about the mess, Darling.
# - But there was already some in the fridge.

pudding_to_eat = make_pudding(ingredients)
pudding_to_eat is fridge_pudding
# => False
Amadan
źródło
3
To może być tylko osobisty gust (gra słów nie jest zamierzona), ale ta analogia była dla mnie bardziej zagmatwana niż pomocna i sprawiła, że ​​chciałem zjeść pudding, gdy nie mam go w lodówce: (Myślę, że odpowiedź Marka Ransoma, choć bardziej nudna, to prawdopodobnie bardziej pouczające
Tom Close
1
@TomClose: Jest wiele dobrych odpowiedzi na to pytanie, wystarczy, że jest miejsce na lekkość. Ja też chcę budyń.
Amadan
5

isi is notsą dwoma operatorami tożsamości w Pythonie. isOperator nie porównuje wartości zmiennych, ale porównuje tożsamości zmiennych. Rozważ to:

>>> a = [1,2,3]
>>> b = [1,2,3]
>>> hex(id(a))
'0x1079b1440'
>>> hex(id(b))
'0x107960878'
>>> a is b
False
>>> a == b
True
>>>

Powyższy przykład pokazuje, że tożsamość (może to być również adres pamięci w Cpythonie) jest różna dla obu ai b(nawet jeśli ich wartości są takie same). Dlatego, gdy mówisz a is b, zwraca fałsz z powodu niedopasowania tożsamości obu operandów. Jednak gdy powiesz a == b, zwraca true, ponieważ ==operacja weryfikuje tylko wtedy, gdy oba operandy mają przypisaną tę samą wartość.

Ciekawy przykład (za dodatkową ocenę):

>>> del a
>>> del b
>>> a = 132
>>> b = 132
>>> hex(id(a))
'0x7faa2b609738'
>>> hex(id(b))
'0x7faa2b609738'
>>> a is b
True
>>> a == b
True
>>>

W powyższym przykładzie, mimo ai bsą to dwie różne zmienne, a is bzwrócone True. Wynika to z faktu typu aIs intktóra jest niezmienna obiekt. Więc python (myślę, że aby zaoszczędzić pamięć) przydzielił ten sam obiekt, bgdy został utworzony z tą samą wartością. Tak więc w tym przypadku tożsamości zmiennych pasowały i a is bokazały się takie True.

Będzie to miało zastosowanie do wszystkich niezmiennych obiektów:

>>> del a
>>> del b
>>> a = "asd"
>>> b = "asd"
>>> hex(id(a))
'0x1079b05a8'
>>> hex(id(b))
'0x1079b05a8'
>>> a is b
True
>>> a == b
True
>>>

Mam nadzieję, że to pomoże.

gixxer
źródło
to jest naprawdę fajny przykład. dzięki za szczegółowe informacje.
Haranadh
Ale spróbuj a = 123456789 b = 123456789
user2183078
Wszystko mniejsze -5lub wyższe niż 256w Pythonie będzie fałszywe. Python buforuje liczby w zakresie [-5, 256].
smart
Nie wszystkie niezmienne obiekty zostaną udostępnione, jak pokazujesz, jest to optymalizacja zastosowana przez środowisko wykonawcze Pythona dla niektórych obiektów, ale nie dla innych. Proces udostępniania małych liczb całkowitych jest dobrze udokumentowany, ale nie sądzę, aby służył do internowania ciągów .
Mark Ransom
4

x is yto samo co id(x) == id(y)porównywanie tożsamości przedmiotów.

Jak zauważył @ tomasz-kurgan w komentarzu poniżej, isoperator zachowuje się nietypowo w przypadku niektórych obiektów.

Na przykład

>>> class A(object):
...   def foo(self):
...     pass
... 
>>> a = A()
>>> a.foo is a.foo
False
>>> id(a.foo) == id(a.foo)
True

Ref;
https://docs.python.org/2/reference/expressions.html#is-not
https://docs.python.org/2/reference/expressions.html#id24

Nizam Mohamed
źródło
Nie, to nieprawda. W większości przypadków może zachowywać się podobnie, ale nie zawsze tak jest. Zobacz to - sam dół strony, punktor 6 .:> (...), możesz zauważyć pozornie nietypowe zachowanie w niektórych zastosowaniach operatora is , na przykład w przypadku porównań między metodami instancji lub stałymi. : `class A (obiekt): def foo (self): pass a = A () print a.foo is a.foo print id (a.foo) == id (a.foo)`
Tomasz Kurgan
3

Jak możesz sprawdzić tutaj do małych liczb całkowitych. Liczby powyżej 257 nie są małymi intami, więc są obliczane jako inny obiekt.

W ==takim przypadku lepiej jest użyć zamiast tego.

Więcej informacji można znaleźć tutaj: http://docs.python.org/2/c-api/int.html

CS Gamer
źródło
2

X wskazuje na tablicę, Y wskazuje na inną tablicę. Te tablice są identyczne, ale isoperator przyjrzy się tym wskaźnikom, które nie są identyczne.

Neko
źródło
5
Python nie ma wskaźników. Musisz zaostrzyć swoją terminologię.
David Heffernan
3
Działa wewnętrznie, podobnie jak Java i wiele innych języków. W rzeczywistości ispokazuje to funkcjonalność operatora.
Neko
5
Szczegóły implementacji nie mają znaczenia. Dokumentacja używa terminologii „tożsamość obiektu”. Ty też powinieneś. „Operatory są i nie są testowane pod kątem tożsamości obiektu: x jest prawdą, jeśli i tylko wtedy, gdy x i y są tym samym obiektem. X nie jest y daje odwrotną wartość prawdziwości.”
David Heffernan
1
@Neko: CPython wewnętrznie używa wskaźników. Ale oczywiście Jython (zaimplementowany w Javie) i PyPy (zaimplementowany w podzbiorze Pythona) nie używają wskaźników. W PyPy niektóre obiekty nie będą miały nawet znaku, idchyba że o to poprosisz.
abarnert
1

Porównuje tożsamość obiektu, to znaczy, czy zmienne odnoszą się do tego samego obiektu w pamięci. To jest jak ==w Javie lub C (porównując wskaźniki).

mipadi
źródło
1

Prosty przykład z owocami

fruitlist = [" apple ", " banana ", " cherry ", " durian "]
newfruitlist = fruitlist
verynewfruitlist = fruitlist [:]
print ( fruitlist is newfruitlist )
print ( fruitlist is verynewfruitlist )
print ( newfruitlist is verynewfruitlist )

Wynik:

True
False
False

Jeśli spróbujesz

fruitlist = [" apple ", " banana ", " cherry ", " durian "]
newfruitlist = fruitlist
verynewfruitlist = fruitlist [:]
print ( fruitlist == newfruitlist )
print ( fruitlist == verynewfruitlist )
print ( newfruitlist == verynewfruitlist )

Wynik jest inny:

True
True
True

Dzieje się tak, ponieważ operator == porównuje tylko zawartość zmiennej. Aby porównać tożsamości dwóch zmiennych, użyj operatora is

Aby wydrukować numer identyfikacyjny:

print ( id( variable ) )
ForsakenOne
źródło
-3

isOperator jest tylko wersja angielska ==. Ponieważ identyfikatory obu list są różne, odpowiedź jest fałszywa. Możesz spróbować:

a=[1,2,3]
b=a
print(b is a )#True

* Ponieważ identyfikatory obu list byłyby takie same

Aadit
źródło
isnie jest „angielską wersją ==
David Buck