Понимание оператора «is» в Python

110

isОператор не совпадают со значениями переменных, но сами экземпляры.

Что это на самом деле значит?

Я объявил две переменные с именами xи yприсвоил одинаковые значения обеим переменным, но он возвращает false, когда я использую isоператор.

Мне нужно разъяснение. Вот мой код.

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

print(x is y)  # It prints false!
анисхан001
источник
Связанный вопрос 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 06
5
Маленькие целые числа интернированы в CPython, так как они используются очень часто. Это оптимизация. x = 5; y = 5; x is y => True, потому что id (x) == id (y). Это тот же целочисленный объект, который используется повторно. Работает в Python, поскольку целые числа неизменяемы. Если вы сделаете x = 1.0; y = 1.0 или x = 9999; y = 9999, это будет не та же идентичность, потому что числа с плавающей запятой и большие целые числа не интернируются.
Magnus Lyckå
1
@ MagnusLyckå есть другие оптимизации, которые могут привести к кэшированию неизменяемых объектов. Например, если вы запустите свой пример в новой функции или вместе с разделяющей точкой с запятой в интерактивном интерпретаторе, вы обнаружите, что у них тоже одинаковый идентификатор.
Мартейн Питерс
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.
Abarnert
источник
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: На этот вопрос есть много хороших ответов, достаточно, чтобы было место для легкомыслия. Еще я хочу пудинг.
Амадан, 01
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он возвращает false из-за несоответствия идентификаторов обоих операндов. Однако, когда вы говорите 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две разные переменные . Это связано с тем, что тип is является неизменяемым объектом. Итак, python (я думаю, чтобы сэкономить память) выделил тот же объект, когда он был создан с тем же значением. Итак, в этом случае идентичности переменных совпали и оказались совпадающими .a is bTrueaintba is bTrue

Это будет применяться ко всем неизменяемым объектам:

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

Надеюсь, это поможет.

Gixxer
источник
это действительно хороший пример. спасибо за подробную информацию.
Haranadh
Но попробуйте a = 123456789 b = 123456789
user2183078 05
Все, что меньше -5или выше, чем 256в Python, будет False. Python кеширует числа в диапазоне [-5, 256].
smart
Не все неизменяемые объекты будут совместно использоваться, как вы показываете, это оптимизация, применяемая средой выполнения 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

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

Низам Мохамед
источник
Нет, это не так. В большинстве случаев он может вести себя одинаково, но это не всегда так. См. Это - в самом низу страницы, подпункт 6 .:> (...), вы можете заметить, казалось бы, необычное поведение в некоторых случаях использования оператора is , например, при сравнении между методами экземпляра или константами. И минимальный рабочий пример. : `class A (object): def foo (self): pass a = A () print a.foo is a.foo print id (a.foo) == id (a.foo)`
Томаш Курган
3

Как вы можете проверить здесь на небольшие целые числа. Числа выше 257 - это не маленькие целые числа, поэтому они рассчитываются как другой объект.

В ==этом случае лучше использовать .

Дополнительная информация здесь: http://docs.python.org/2/c-api/int.html

CS Gamer
источник
2

X указывает на массив, Y указывает на другой массив. Эти массивы идентичны, но isоператор будет смотреть на эти указатели, которые не идентичны.

Неко
источник
5
У Python нет указателей. Вам нужно ужесточить терминологию.
Дэвид Хеффернан
3
Он работает внутренне, как Java и многие другие языки. Собственно, функциональность isоператора это показывает.
Neko
5
Детали реализации не имеют значения. В документации используется терминология «идентичность объекта». Вы тоже должны. «Операторы являются и не проверяют идентичность объекта: x is y истинно тогда и только тогда, когда x и y являются одним и тем же объектом. X is not 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

Это потому, что оператор == сравнивает только содержимое переменной. Для сравнения идентичностей двух переменных используйте is оператор

Чтобы распечатать идентификационный номер:

print ( id( variable ) )
ForsakenOne
источник
-3

isОператор не что иное, как версия английский язык ==. Поскольку идентификаторы двух списков разные, ответ неверен. Можешь попробовать:

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

* Потому что идентификаторы обоих списков будут одинаковыми

Aadit
источник
isне является «английской версией ==»
Дэвид Бак