Почему сравнение строк с использованием '==' или 'is' иногда дает другой результат?

1147

У меня есть программа на Python, где две переменные установлены в значение 'public'. В условном выражении у меня есть сравнение, var1 is var2которое не удается, но если я изменяю его, var1 == var2оно возвращает True.

Теперь, если я открою свой интерпретатор Python и проведу такое же сравнение «как есть», это будет успешно.

>>> s1 = 'public'
>>> s2 = 'public'
>>> s2 is s1
True

Что мне здесь не хватает?

jottos
источник
8
см .: stackoverflow.com/questions/1392433/…
Ник Дандулакис
3
Эта проблема также возникает , когда вы читаете консольный ввод с помощью , например: input = raw_input("Decide (y/n): "). В этом случае вход «y» и if input == 'y':вернет «True», в то время как if input is 'y':вернет False.
Семжон Мессингер
4
Этот блог дает гораздо более полное объяснение, чем любой ответ guilload.com/python-string-interning
Chris_Rands
1
Как упоминает @ chris-rico, я
привожу
3
Возможный дубликат Есть ли разница между `==` и `is` в Python?
Такнок

Ответы:

1533

is это тестирование личности, == это проверка на равенство. то, что происходит в вашем коде, будет эмулироваться в интерпретаторе следующим образом:

>>> a = 'pub'
>>> b = ''.join(['p', 'u', 'b'])
>>> a == b
True
>>> a is b
False

так что неудивительно, что они не одинаковы, верно?

Другими словами: isэтоid(a) == id(b)

SilentGhost
источник
17
ах, как и эк? против равных? в схеме, понял.
jottos
47
Или ==против .equals()Java. Самое приятное, что Python ==не аналогичен Java ==.
MatrixFrog
11
@ Крайст: есть только одно Noneзначение. Так что у него всегда один и тот же идентификатор.
SilentGhost
18
Это не относится к примеру OP "is -> True".
user2864740 10.09.14
6
@AlexanderSupertramp, из-за интернирования строк .
Крис Рико
570

Другие ответы здесь верны: isиспользуется для сравнения идентичности , а ==для сравнения на равенство . Поскольку вас волнует равенство (две строки должны содержать одинаковые символы), в этом случае isоператор просто неверен, и вы должны использовать ==вместо него.

isИнтерактивная причина заключается в том, что (большинство) строковых литералов интернированы по умолчанию. Из Википедии:

Срочные строки ускоряют сравнение строк, которые иногда являются узким местом производительности в приложениях (таких как компиляторы и динамические языки программирования), которые в значительной степени зависят от хеш-таблиц со строковыми ключами. Без интернирования проверка на то, что две разные строки равны, включает проверку каждого символа обеих строк. Это медленно по нескольким причинам: это по сути O (n) в длине строк; обычно требуется чтение из нескольких областей памяти, что занимает время; и чтение заполняет кэш процессора, что означает, что для других нужд кэш-памяти меньше. С интернированными строками достаточно простого теста идентичности объекта после исходной операции интерна; это обычно реализуется как тест на равенство указателей,

Таким образом, если в вашей программе есть два строковых литерала (слова, которые буквально набираются в исходный код вашей программы, заключенные в кавычки), которые имеют одинаковое значение, компилятор Python автоматически интернирует строки, делая их оба хранящимися в одном и том же место в памяти. (Обратите внимание, что это не всегда происходит, и правила, когда это происходит, довольно запутаны, поэтому, пожалуйста, не полагайтесь на это поведение в рабочем коде!)

Поскольку в вашем интерактивном сеансе обе строки на самом деле хранятся в одной и той же ячейке памяти, они имеют одинаковую идентичность , поэтому isоператор работает, как и ожидалось. Но если вы строите строку каким-либо другим методом (даже если эта строка содержит точно такие же символы), тогда строка может быть одинаковой , но это не та же строка, то есть она имеет другую идентичность , потому что она хранится в другом месте в памяти.

Даниэль Приден
источник
6
Где кто-то может прочитать больше о запутанных правилах для интернированных строк?
Noctis Skytower
89
+1 для подробного объяснения. Не уверен, как другой ответ получил так много голосов, не объяснив, что на самом деле произошло.
That1Guy
4
это именно то, о чем я думал, когда читал вопрос. Принятый ответ является коротким, но содержит факт, но этот ответ объясняет вещи намного лучше. Ницца!
Snađошƒаӽ
3
@NoctisSkytower Гуглил тоже самое и нашел этот guilload.com/python-string-interning
xtreak
5
@ naught101: Нет, правило состоит в том, чтобы выбирать между ==и isна основе того, какую проверку вы хотите. Если вы заботитесь о том, чтобы строки были одинаковыми (то есть имели одинаковое содержимое), вам всегда следует использовать ==. Если вы заботитесь о том, относятся ли любые два имени Python к одному и тому же экземпляру объекта, вы должны использовать is. Вам может понадобиться, isесли вы пишете код, который обрабатывает множество различных значений, не заботясь об их содержимом, или если вы знаете, что есть только одно из чего-то, и хотите игнорировать другие объекты, притворяющиеся этим. Если вы не уверены, всегда выбирайте ==.
Даниэль Приден
108

isКлючевое слово тест идентичности объекта при== сравнение значения.

Если вы используете is, результат будет истинным, если и только если объект является тем же объектом. Тем не менее, ==будет истина всякий раз, когда значения объекта совпадают.

Томас Оуэнс
источник
57

И последнее, что следует отметить, вы можете использовать sys.internфункцию, чтобы убедиться, что вы получаете ссылку на ту же строку:

>>> from sys import intern
>>> a = intern('a')
>>> a2 = intern('a')
>>> a is a2
True

Как указано выше, вы не должны использовать isдля определения равенства строк. Но это может быть полезно знать, если у вас есть какое-то странное требование использоватьis .

Обратите внимание, что internфункция была встроенной в Python 2, но была перенесена в sysмодуль в Python 3.

Джейсон Бейкер
источник
43

isэто проверка идентичности, ==это проверка на равенство. Это означает, что isэто способ проверить, являются ли две вещи одинаковыми или просто эквивалентными.

Скажем, у вас есть простой personобъект. Если его зовут Джек и ему 23 года, это эквивалентно другому 23-летнему Джеку, но это не тот же человек.

class Person(object):
   def __init__(self, name, age):
       self.name = name
       self.age = age

   def __eq__(self, other):
       return self.name == other.name and self.age == other.age

jack1 = Person('Jack', 23)
jack2 = Person('Jack', 23)

jack1 == jack2 #True
jack1 is jack2 #False

Они одного возраста, но это не один и тот же человек. Строка может быть эквивалентна другой, но это не тот же объект.

TankorSmash
источник
Если вы измените набор jack1.age = 99, это не изменится jack2.age. Это потому, что это два разных экземпляра jack1 is not jack2. Однако они могут сравняться друг с другом, jack1 == jack2если их имя и возраст совпадают. Это становится более сложным для строк, потому что строки неизменны в Python, и Python часто использует один и тот же экземпляр. Мне нравится это объяснение, потому что оно использует простые случаи (обычный объект), а не специальные случаи (строки).
Flimm
37

Это примечание, но в идиоматическом питоне вы часто увидите такие вещи, как:

if x is None: 
    # some clauses

Это безопасно, потому что гарантированно будет один экземпляр Null Object (т. Е. None) .

Грегг Линд
источник
1
Верно ли то же самое для Истины и Ложи? Только один экземпляр так будет совпадать?
HandyManDan
1
@HandyManDan Да, они синглтоны как в
питоне
@kamillitw, но в Python 2 вы можете переназначить False и True.
Мартин Питерс
28

Если вы не уверены, что делаете, используйте «==». Если у вас есть немного больше знаний об этом, вы можете использовать «is» для известных объектов, таких как «None».

В противном случае вы будете удивляться, почему что-то не работает и почему это происходит:

>>> a = 1
>>> b = 1
>>> b is a
True
>>> a = 6000
>>> b = 6000
>>> b is a
False

Я даже не уверен, что некоторые вещи будут оставаться одинаковыми между различными версиями / реализациями Python.

Маттиас Нильссон
источник
1
Интересный пример, показывающий, как переназначение целых вызывает срабатывание этого условия. Почему это не удалось? Это из-за стажировки или что-то еще?
Пол
Похоже, причина, по которой это возвращает false, может быть из-за реализации интерпретатора: stackoverflow.com/questions/132988/…
Пол
@ArchitJain Да, эти ссылки объясняют это довольно хорошо. Когда вы их прочитаете, вы узнаете, на каких числах вы можете использовать «есть». Я просто хотел бы, чтобы они объяснили, почему это по-прежнему не очень хорошая идея. Вы знаете, что это не делает хорошей идеей предполагать, что все остальные тоже так поступают (или что интернализованный диапазон номеров никогда не изменится)
Маттиас Нильссон
20

Из моего ограниченного опыта работы с python, isиспользуется для сравнения двух объектов, чтобы увидеть, являются ли они одним и тем же объектом, в отличие от двух разных объектов с одинаковым значением. ==используется для определения идентичности значений

Вот хороший пример:

>>> s1 = u'public'
>>> s2 = 'public'
>>> s1 is s2
False
>>> s1 == s2
True

s1является строкой Unicode и s2является нормальной строкой. Они не одного типа, но имеют одинаковое значение.

Джек М.
источник
17

Я думаю, что это связано с тем фактом, что когда сравнение «is» оценивается как ложное, используются два разных объекта. Если он оценивается как true, это означает, что внутри он использует один и тот же точный объект и не создает новый, возможно, потому, что вы создали их в течение доли или около того секунд, и потому что между оптимизацией и оптимизацией нет большого промежутка времени. использует тот же объект.

Вот почему вы должны использовать оператор равенства ==, isчтобы сравнивать значение строкового объекта.

>>> s = 'one'
>>> s2 = 'two'
>>> s is s2
False
>>> s2 = s2.replace('two', 'one')
>>> s2
'one'
>>> s2 is s
False
>>> 

В этом примере я сделал s2, который был другим строковым объектом, ранее равным 'one', но это не тот же объект s, что и потому, что интерпретатор не использовал тот же объект, поскольку я изначально не назначал его 'one', если бы у меня это было, они бы сделали один и тот же объект.

медер омуралиев
источник
3
Использование .replace()в качестве примера в этом контексте, вероятно, не самое лучшее, потому что его семантика может сбивать с толку. всегдаs2 = s2.replace() будет создавать новый строковый объект, назначать новый строковый объект , а затем избавляться от строкового объекта, который использовался для указания. Таким образом, даже если бы вы это сделали, вы все равно получили бы новый строковый объект. s2s2s = s.replace('one', 'one')
Даниэль Приден
13

Я считаю, что это известно как "интернированные" строки. Это делает и Python, и Java, и C и C ++ при компиляции в оптимизированных режимах.

Если вы используете две одинаковые строки вместо того, чтобы тратить память на создание двух строковых объектов, все интернированные строки с одинаковым содержимым указывают на одну и ту же память.

Это приводит к тому, что оператор Python «is» возвращает True, потому что две строки с одинаковым содержимым указывают на один и тот же строковый объект. Это также произойдет в Java и C.

Это полезно только для экономии памяти. Вы не можете полагаться на него для проверки на равенство строк, потому что различные интерпретаторы и компиляторы и механизмы JIT не всегда могут это сделать.

Зан Рысь
источник
12

Я отвечаю на вопрос, несмотря на то, что вопрос старый, потому что ни один ответ не цитирует ссылку на язык

На самом деле оператор is проверяет идентичность, а оператор == проверяет равенство,

Из справочника языка:

Типы влияют практически на все аспекты поведения объекта. Даже важность идентичности объекта зависит в некотором смысле: для неизменяемых типов операции, которые вычисляют новые значения, могут фактически возвращать ссылку на любой существующий объект с тем же типом и значением, в то время как для изменяемых объектов это недопустимо. . Например, после а = 1; b = 1, a и b могут или не могут ссылаться на один и тот же объект со значением один, в зависимости от реализации, но после c = []; d = [], c и d гарантированно ссылаются на два разных, уникальных, недавно созданных пустых списка. (Обратите внимание, что c = d = [] назначает один и тот же объект как c, так и d.)

Таким образом, из приведенного выше утверждения мы можем сделать вывод, что строки, которые являются неизменяемым типом, могут завершаться с ошибкой при проверке с помощью «is» и могут быть проверены успешно при проверке с помощью «is»

То же самое относится к int, кортежу, которые также являются неизменяемыми типами.

Баран
источник
8

==Тестовое значение оператора эквивалентности. isИдентичность тесты оператора объекта, тесты Python ли два действительно тот же объект (то есть, жить по тому же адресу в памяти).

>>> a = 'banana'
>>> b = 'banana'
>>> a is b 
True

В этом примере Python создал только один строковый объект, и оба, aи bссылаются на него. Причина в том, что Python внутренне кэширует и повторно использует некоторые строки в качестве оптимизации, на самом деле в памяти есть просто строка «банан», совместно используемая a и b; Чтобы вызвать нормальное поведение, вам нужно использовать более длинные строки:

>>> a = 'a longer banana'
>>> b = 'a longer banana'
>>> a == b, a is b
(True, False)

Когда вы создаете два списка, вы получаете два объекта:

>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a is b
False

В этом случае мы бы сказали, что два списка эквивалентны, потому что они имеют одинаковые элементы, но не идентичны, потому что они не являются одним и тем же объектом. Если два объекта идентичны, они также эквивалентны, но если они эквивалентны, они не обязательно идентичны.

Если aссылается на объект, и вы назначаете b = a, то обе переменные ссылаются на один и тот же объект:

>>> a = [1, 2, 3]
>>> b = a
>>> b is a
True
X. Ван
источник
7

isбудет сравнивать расположение памяти. Используется для сравнения на уровне объекта.

==будет сравнивать переменные в программе. Используется для проверки на уровне значения.

is проверяет эквивалентность на уровне адресов

== проверяет эквивалентность на уровне значений

johnashu
источник
3

isтестирование идентичности, тестирование на ==равенство (см. документацию Python ).

В большинстве случаев, если a is b, тогда a == b. Но есть исключения, например:

>>> nan = float('nan')
>>> nan is nan
True
>>> nan == nan
False

Таким образом, вы можете использовать только isдля тестов идентичности, но не тесты на равенство.

Райан
источник