Розуміння оператора Python "є"


110

isОператор не збігаються зі значеннями змінних, але самі екземпляри.

Що це насправді означає?

Я оголосив дві змінні імена xта yпризначив однакові значення обом змінним, але вона повертає помилкові, коли я використовую isоператор.

Мені потрібно уточнення. Ось мій код.

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

print(x is y)  # It prints false!

Пов'язані питання stackoverflow.com/questions/38189660 / ...
Kasravnd

Відповіді:


181

Ви неправильно зрозуміли, що isтестує оператор. Він перевіряє, чи дві змінні вказують на один об'єкт , а не якщо дві змінні мають одне значення.

З документації для isоператора :

Оператори isта is notтест на ідентичність об'єкта: x is yістинно, якщо і лише тоді, коли xі yє одним і тим же об'єктом.

Використовуйте ==оператор замість цього:

print(x == y)

Це відбитки True. xі yє двома окремими списками:

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

Якщо ви використовуєте цю id()функцію, ви побачите це xі yмаєте різні ідентифікатори:

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

але якщо ви повинні були призначити yна xте обидві точки до того ж об'єкту:

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

і isпоказує, що обидва є одним і тим же об'єктом, він повертається True.

Пам'ятайте, що в Python імена є лише мітками, що посилаються на значення ; ви можете мати кілька імен, що вказують на один і той же об’єкт. isповідомляє, якщо два імені вказують на один і той самий об’єкт. ==повідомляє, якщо два імені стосуються об'єктів, які мають однакове значення.


13
Отже, A is Bте саме, що id(A) == id(B).
imallett

2
@imallett: це проксі-сервер для того ж тесту, за умови, що ви не зберігаєте id(A)змінну і пізніше очікуєте, variable == id(B)що вона все ще працюватиме; якщо Aтим часом було видалено, то Bмогло б бути надано те саме місце пам'яті.
Martijn Pieters

1
Не вдалося відформатувати коментарі. Але є цікава річ. :) >>> x = 5 \n>>> y = 5 \n>>> x is y \nTrue \n>>> x == y \nTrue \n>>>\n
Haranadh

5
Малі цілі числа інтерновані в CPython, оскільки вони використовуються так часто. Це оптимізація. x = 5; y = 5; x є y => Істинно, тому що id (x) == id (y). Це той самий цілий об'єкт, який повторно використовується. Працює в Python, оскільки цілі числа незмінні. Якщо ви x = 1,0; y = 1.0 або x = 9999; y = 9999, це не буде однаковою ідентичністю, оскільки поплавці та великі вставки не інтерновані.
Магнус Ліке

1
@ MagnusLyckå є й інші оптимізації, які можуть спричинити кешування незмінних об'єктів. Наприклад, якщо ви запустите свій приклад в новій функції або разом з роздільною крапкою з комою в інтерактивному інтерпретаторі, ви виявите, що вони також мають той самий ідентифікатор.
Martijn Pieters

60

Інший дублікат запитував, чому два рівних рядки взагалі не однакові, на що насправді тут не відповіли:

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

Отже, чому вони не є однією строкою? Особливо враховуючи це:

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

Давайте трохи відкладемо другу частину. Як перша може бути правдою?

Інтерпретатор повинен мати "таблицю інтернування", таблицю зіставленням рядкових значень для рядкових об'єктів, тому кожен раз, коли ви намагаєтесь створити нову рядок із вмістом 'abc', ви отримуєте назад той самий об'єкт. У Вікіпедії є більш детальне обговорення того, як працює стажування.

І у Python є стіл для інтернування; ви можете вручну інтернувати рядки sys.internметодом.

Насправді Python дозволено автоматично стажувати будь-які незмінні типи, але цього не вимагає . У різних реалізаціях будуть інтернуватися різні значення.

CPython (реалізація, яку ви використовуєте, якщо ви не знаєте, яку саме програму ви використовуєте) автоматично інтернірує невеликі цілі числа та деякі спеціальні сингтони, такі як False, але не рядки (або великі цілі числа, або маленькі кортежі, або щось інше). Ви можете це легко побачити:

>>> 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

Гаразд, але чому були zі wоднакові?

Це не інтерпретатор, який автоматично стажується, це значення компілятора.

Якщо ж у час компіляції рядок з'являється двічі в одному модулі (що саме це означає, важко визначити, що це не те ж саме , як строковий літерал, тому що r'abc', 'abc'і 'a' 'b' 'c'всі різні літерали , але та ж рядок, але легко зрозуміти , інтуїтивно) компілятор створить лише один екземпляр рядка з двома посиланнями.

Насправді компілятор може піти ще далі: 'ab' + 'c'може бути перетворений 'abc'оптимізатором, і в цьому випадку він може бути складений разом із 'abc'константою в одному модулі.

Знову ж таки, це Python дозволено, але цього не потрібно робити. Але в цьому випадку CPython завжди складається з невеликих рядків (а також, наприклад, маленьких кортежів). .


Отже, що ви повинні зробити з цим як програміст?

Ну ... нічого. Ви майже ніколи не маєте підстав піклуватися про те, чи є дві незмінні значення однакові. Якщо ви хочете знати, коли ви можете використовувати a is bзамість цього a == b, ви задаєте неправильне запитання. Просто завжди використовуйте, a == bза винятком двох випадків:

  • Для більш зрозумілого порівняння значень одиночного типу x is None.
  • Щодо змінних значень, коли вам потрібно знати, чи xвплине мутація на y.

1
Відмінне пояснення, особливо ваші поради наприкінці.
DavidG

Дякую за детальне пояснення. Хтось знає: якщо wі zє однаковими через значення складання компілятора, чому це також працює в REPL, навіть використовуючи id()для перевірки посилань? Використання REPL на Python 3.7
Chi-chi

8

isповертає істину лише у тому випадку, якщо вони насправді однакові. Якби вони були однакові, зміна в одній з’явилася б і в іншій. Ось приклад різниці.

>>> 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]

8

Ця аналогія може запропонувати повторити запитання :

# - 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

3
Це може бути лише особистий смак (каламбур не призначений), але я вважав цю аналогію більш заплутаною, ніж корисною, і змусив мене їсти пудинг, коли у мене немає холодильника :( Я думаю, що відповідь Марка Рансома, хоча і більш нудна, - напевно, більш повчальний
Том Закрити

1
@TomClose: На це питання є багато тонких відповідей, достатньо, щоб було місце для легкістю. Також я хочу також пудинг.
Амадан

5

isі is notє двома операторами ідентичності в Python. isОператор не порівнює значення змінних, але порівнює ідентичності змінних. Врахуйте це:

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

Наведений вище приклад показує, що ідентичність (може також бути адресою пам'яті в Cpython) різна для обох aі b(хоча їх значення однакові). Ось чому, коли ви говорите, a is bвін повертає хибність через невідповідність тотожностей обох операндів. Однак, коли ви говорите a == b, він повертає істину, оскільки ==операція перевіряє, чи мають обоє операнди однакове значення, призначене їм.

Цікавий приклад (для додаткового класу):

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

У наведеному вище прикладі, хоча aі bє двома різними змінними, a is bповернулися True. Це пояснюється тим, що тип об'єкта aє intнезмінним об'єктом. Тож python (я думаю, щоб зберегти пам'ять) виділив той самий об’єкт, bколи він був створений з тим же значенням. Тож у цьому випадку тотожність змінних збігалася і a is bвиявилася такою True.

Це стосується всіх незмінних об'єктів:

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

Сподіваюся, що це допомагає.


це справді приємний приклад. дякую за детальну інформацію
Харанад

Але спробуйте a = 123456789 b = 123456789
user2183078

Все, що менше -5або вище, ніж 256у Python, буде помилковим. Python кешує числа в діапазоні [-5, 256].
розумний

Не всі незмінні об’єкти будуть спільними, як ви показуєте, це оптимізація, застосована програмою Python для деяких об'єктів, але не для інших. Процес спільного використання малих цілих чисел добре задокументований, але я не думаю, що це для строкового інтернування .
Марк Викуп

4

x is yте саме, що id(x) == id(y), порівнюючи ідентичність предметів.

Як зазначає @ tomasz-kurgan у коментарі нижче, isоператор поводиться незвично з певними об'єктами.

Напр

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

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


Ні, це не так. Він може поводитись подібним у більшості випадків, але це не завжди так. Дивіться це - в самому низу сторінки, куля 6.:> (...), ви можете помітити , на перший погляд незвичайного поведінки в певних видах використання є оператором, як і тим , які включають порівняння між методами примірника, або константами і мінімальним робочим прикладом : `клас A (об'єкт): def foo (self): передайте a = A () print a.foo is a.foo print id (a.foo) == id (a.foo)`
Tomasz Kurgan

3

Як ви можете перевірити тут на малі цілі числа. Числа понад 257 - це не малі точки, тому він обчислюється як інший об'єкт.

Краще використовувати ==в цьому випадку замість цього.

Додаткову інформацію можна знайти тут: http://docs.python.org/2/c-api/int.html


2

X вказує на масив, Y вказує на інший масив. Ці масиви ідентичні, але isоператор буде дивитись на ті покажчики, які не є ідентичними.


5
У Python немає покажчиків. Вам потрібно посилити свою термінологію.
Девід Геффернан

3
Це робиться внутрішньо, як і Java, і так багато інших мов. Фактично isце показує функціональність оператора.
Неко

5
Деталі впровадження не важливі. У документації використовується термінологія "ідентифікація об'єкта". Так і слід. "Оператори є і не є тестом на ідентичність об'єкта: x є y істинно, якщо і тільки тоді, коли x і y є одним і тим же об'єктом. X не y дає значення зворотної істини."
Девід Геффернан

1
@Neko: CPython використовує внутрішньо покажчики. Але очевидно, що Jython (реалізований у Java) та PyPy (реалізований у підмножині Python) не використовують покажчики. У PyPy деякі об’єкти навіть не матимуть, idякщо ви цього не попросите.
abarnert

1

Він порівнює ідентичність об'єкта, тобто чи змінні посилаються на один і той же об'єкт у пам'яті. Це як ==у Java або C (при порівнянні покажчиків).


1

Простий приклад з фруктами

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

Вихід:

True
False
False

Якщо ви спробуєте

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

Вихід різний:

True
True
True

Це тому, що оператор == порівнює лише вміст змінної. Для порівняння тотожності 2 змінного використання є оператором

Щоб надрукувати ідентифікаційний номер:

print ( id( variable ) )

-3

isОператор не що інше, як версія англійську мову ==. Оскільки ідентифікатори двох списків різні, то відповідь хибна. Ви можете спробувати:

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

* Оскільки ідентифікатори обох списків були б однаковими


isне є "англійською версією =="
Девід Бак
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.