Есть ли разница между «==» и «есть»?

630

Мой Google-фу подвел меня.

В Python следующие два теста на равенство эквивалентны?

n = 5
# Test one.
if n == 5:
    print 'Yay!'

# Test two.
if n is 5:
    print 'Yay!'

Верно ли это для объектов, где вы будете сравнивать экземпляры ( listскажем)?

Итак, этот вид ответа на мой вопрос:

L = []
L.append(1)
if L == [1]:
    print 'Yay!'
# Holds true, but...

if L is [1]:
    print 'Yay!'
# Doesn't.

Итак, ==тесты имеют значение, где isтесты, чтобы увидеть, являются ли они одним и тем же объектом?

Бернард
источник

Ответы:

928

isвернется, Trueесли две переменные указывают на один и тот же объект, ==если объекты, на которые ссылаются переменные, равны.

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

# Make a new copy of list `a` via the slice operator, 
# and assign it to variable `b`
>>> b = a[:] 
>>> b is a
False
>>> b == a
True

В вашем случае второй тест работает только потому, что Python кэширует небольшие целочисленные объекты, что является деталью реализации. Для больших целых чисел это не работает:

>>> 1000 is 10**3
False
>>> 1000 == 10**3
True

То же самое верно для строковых литералов:

>>> "a" is "a"
True
>>> "aa" is "a" * 2
True
>>> x = "a"
>>> "aa" is x * 2
False
>>> "aa" is intern(x*2)
True

Пожалуйста, смотрите этот вопрос также.

Торстен Марек
источник
2
Я обнаружил , что: echo 'import sys;tt=sys.argv[1];print(tt is "foo", tt == "foo", id(tt)==id("foo"))'| python3 - fooвыход: False True False.
Ахуиго
Вы потеряли меня с b = a[:]частью копирования списка операторов слайсов, поэтому я отредактировал ваш ответ, чтобы оставить там комментарий. Похоже, я только что достиг порога, чтобы не нужно было пересматривать мои правки, прежде чем они будут применены, так что, надеюсь, это здорово с вами. Независимо от этого, вот полезный справочник о том, как копировать списки, на которые я натолкнулся и на которые мне пришлось ссылаться, чтобы выяснить, что вы делаете: stackoverflow.com/a/2612815/4561887
Габриэль Стейплз
Другой способ продемонстрировать разницу заключается в сравнении объектов разных типов, которые, конечно, никогда не могут быть одним и тем же объектом, но все равно сравниваются при использовании ==. Так, 5.0например, значение с плавающей запятой, а 5целое число. Но 5.0 == 5все равно вернется, Trueпотому что они представляют одну и ту же ценность. С точки зрения производительности и утки, isон всегда проверяется интерпретатором, сравнивая адреса памяти операнда, в то время ==как объект сам решает, определяет ли он себя как что-то еще.
Бахсау
3
1000 is 10**3оценивается как True в Python 3.7, поскольку 10 ** 3 является типом int. Но 1000 is 1e3оценивается как False, поскольку 1e3 является типом float.
Ахмед Фасих
@AhmedFasih Значение 1000 is 10**3true зависит от реализации и зависит от предварительной оценки выражения компилятором 10**3. x=10; 1000 is x**3оценивает до False.
Чепнер
314

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

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

В общем, когда вы сравниваете что-то с простым типом, вы обычно проверяете равенство значений , поэтому вам следует использовать его ==. Например, цель вашего примера, вероятно, состоит в том, чтобы проверить, имеет ли x значение, равное 2 ( ==), а не указывает ли xбуквально на тот же объект, что и 2.


Еще кое-что, на что следует обратить внимание: из-за того, как работает реализация ссылок CPython, вы получите неожиданные и противоречивые результаты, если по ошибке будете использовать isдля сравнения ссылок на целые числа:

>>> a = 500
>>> b = 500
>>> a == b
True
>>> a is b
False

Это в значительной степени то, что мы ожидали: aи bимеют одинаковую ценность, но являются различными объектами. Но как насчет этого?

>>> c = 200
>>> d = 200
>>> c == d
True
>>> c is d
True

Это не согласуется с более ранним результатом. Что тут происходит? Оказывается, эталонная реализация Python кэширует целочисленные объекты в диапазоне -5..256 как единичные экземпляры по соображениям производительности. Вот пример, демонстрирующий это:

>>> for i in range(250, 260): a = i; print "%i: %s" % (i, a is int(str(i)));
... 
250: True
251: True
252: True
253: True
254: True
255: True
256: True
257: False
258: False
259: False

Это еще одна очевидная причина не использовать is: поведение оставлено на усмотрение реализации, когда вы ошибочно используете его для равенства значений.

Джон Феминелла
источник
Что касается первого примера a=500и b=500, просто хотел бы отметить, что если вы установите aи bинтергер между [-5, 256], на a is bсамом деле возвращает True. Больше информации здесь: stackoverflow.com/q/306313/7571052
AsheKetchum
1
@AsheKetchum, да, обратите внимание, что я написал: «Из соображений производительности получается, что эталонная реализация целочисленных объектов кэша Python в диапазоне -5..256 является единичным экземпляром».
Джон Феминелла
34

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

stephenbayer
источник
32

Есть ли разница между ==и isв Python?

Да, у них есть очень важное различие.

==: проверьте на равенство - семантика состоит в том, что эквивалентные объекты (которые не обязательно являются одним и тем же объектом) будут проверяться как равные. Как сказано в документации :

Операторы <,>, ==,> =, <= и! = Сравнивают значения двух объектов.

is: проверьте идентичность - семантика заключается в том, что объект (как хранится в памяти) является объектом. Опять же, в документации сказано :

Операторы isи is notтест для идентификации объекта: x is yистинно тогда и только тогда, когда xи yявляются одним и тем же объектом. Идентичность объекта определяется с помощью id()функции. x is not yдает обратное значение истины.

Таким образом, проверка на идентичность аналогична проверке на равенство идентификаторов объектов. Это,

a is b

такой же как:

id(a) == id(b)

где id- встроенная функция, которая возвращает целое число, которое «гарантированно будет уникальным среди одновременно существующих объектов» (см. help(id)) и где aи b- любые произвольные объекты.

Другие направления использования

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

В общем, мы используем isдля проверки личности. Это обычно полезно, когда мы проверяем объект, который должен существовать только один раз в памяти, называемый в документации «синглтоном».

Варианты использования для isвключают в себя:

  • None
  • значения enum (при использовании Enums из модуля enum)
  • обычно модули
  • обычно объекты класса, полученные из определений классов
  • обычно функциональные объекты, вытекающие из определений функций
  • все остальное, что должно существовать в памяти только один раз (как правило, все синглтоны)
  • конкретный объект, который вы хотите по идентичности

Обычные варианты использования ==включают в себя:

  • числа, включая целые числа
  • строки
  • списки
  • наборы
  • словари
  • пользовательские изменяемые объекты
  • другие встроенные неизменяемые объекты, в большинстве случаев

Общий случай использования, опять же , для ==, это объект , который вы хотите , не может быть тот же объект, а это может быть эквивалентно один

PEP 8 направлений

PEP 8, официальное руководство по стилю Python для стандартной библиотеки, также упоминает два варианта использования дляis :

Сравнения с синглетонами, такими как Noneвсегда, должны выполняться с isили is notникогда, а не с операторами равенства.

Кроме того, остерегайтесь писать, if xкогда вы действительно имеете в виду if x is not None- например, когда проверяете, было ли для переменной или аргумента по умолчанию None задано какое-то другое значение. Другое значение может иметь тип (например, контейнер), который может быть ложным в логическом контексте!

Вывод равенства из идентичности

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

В большинстве случаев эта логика верна, но она опирается на реализацию __eq__специального метода. Как говорят документы ,

Поведение по умолчанию для сравнения равенства ( ==и !=) основано на идентичности объектов. Следовательно, сравнение равенства экземпляров с одинаковой идентичностью приводит к равенству, а сравнение равенства экземпляров с разными идентичностями приводит к неравенству. Мотивация для этого поведения по умолчанию - желание, чтобы все объекты были рефлексивными (то есть, х есть у, подразумевает х == у).

и в интересах последовательности рекомендует:

Сравнение равенства должно быть рефлексивным. Другими словами, идентичные объекты должны сравниваться равными:

x is y подразумевает x == y

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

>>> class Object(object): pass
>>> obj = Object()
>>> obj2 = Object()
>>> obj == obj, obj is obj
(True, True)
>>> obj == obj2, obj is obj2
(False, False)

Противоположное также обычно верно - если что-то проверяется как не равное, вы можете сделать вывод, что это не один и тот же объект.

Поскольку тесты на равенство могут быть настроены, этот вывод не всегда верен для всех типов.

Исключение

Заметное исключение nan- оно всегда тестируется как не равное себе:

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

Проверка на идентичность может быть намного быстрее, чем проверка на равенство (что может потребовать рекурсивной проверки членов).

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

Обратите внимание, что сравнение равенства списков и кортежей предполагает, что идентичность объектов одинакова (потому что это быстрая проверка). Это может создать противоречия, если логика противоречива - как это для nan:

>>> [nan] == [nan]
True
>>> (nan,) == (nan,)
True

Поучительная история:

Вопрос пытается использовать isдля сравнения целых чисел. Не следует предполагать, что экземпляр целого числа совпадает с экземпляром, полученным по другой ссылке. Эта история объясняет почему.

У комментатора был код, который основывался на том факте, что маленькие целые числа (от -5 до 256 включительно) в Python являются синглетонами вместо проверки на равенство.

Вау, это может привести к некоторым коварным ошибкам. У меня был какой-то код, который проверял, является ли a b, и работал, как я хотел, потому что a и b, как правило, небольшие числа. Ошибка произошла только сегодня, после шести месяцев работы, потому что a и b были достаточно большими, чтобы их нельзя было кэшировать. - GWG

Это работало в разработке. Возможно, прошло несколько юнит-тестов.

И он работал в производстве - до тех пор, пока код не проверил целое число больше 256, и в этот момент он не заработал.

Это производственный сбой, который мог быть обнаружен при проверке кода или, возможно, при проверке стиля.

Позвольте мне подчеркнуть: не используйте isдля сравнения целых чисел.

Аарон Холл
источник
"не используйте это вообще" было бы хорошим правилом тоже. Идиоматика, is Noneявляющаяся исключением, но это == Noneтоже работает ...
Жан-Франсуа Фабр
@ Jean-FrançoisFabre Еще одно исключение: официальная документация рекомендует использовать s isдля сравнения Enum.
Артур
@ Артур Я добавил список вариантов использования ...
Аарон Холл
19

Какая разница между isа ==?

==и isразные сравнения! Как уже говорили другие:

  • == сравнивает значения объектов.
  • is сравнивает ссылки объектов.

В Python имена ссылаются на объекты, например, в этом случае value1и value2ссылаются на intэкземпляр, хранящий значение 1000:

value1 = 1000
value2 = value1

введите описание изображения здесь

Потому что value2относится к тому же объекту isи ==даст True:

>>> value1 == value2
True
>>> value1 is value2
True

В следующем примере имена value1и value2ссылаются на разные intэкземпляры, даже если оба хранят одно и то же целое число:

>>> value1 = 1000
>>> value2 = 1000

введите описание изображения здесь

Поскольку то же самое значение (целое число) хранятся ==будет True, поэтому его часто называют «сравнением значений». Однако isвернется, Falseпотому что это разные объекты:

>>> value1 == value2
True
>>> value1 is value2
False

Когда использовать что?

Как правило is, это гораздо более быстрое сравнение. Вот почему CPython кэширует (или, возможно, повторное использование будет лучшим термином) определенные объекты, такие как маленькие целые числа, некоторые строки и т. Д. Но это следует рассматривать как детали реализации, которые могут (даже если вряд ли) измениться в любой момент без предупреждения.

Вы должны использовать толькоis если вы:

  • хочу проверить, действительно ли два объекта - это один и тот же объект (а не одно и то же «значение»). Один пример может быть, если вы используете одноэлементный объект в качестве константы.
  • хочу сравнить значение с константой Python . Константы в Python:

    • None
    • True1
    • False1
    • NotImplemented
    • Ellipsis
    • __debug__
    • классы (например int is intили int is float)
    • во встроенных модулях или сторонних модулях могут быть дополнительные константы. Например np.ma.maskedиз модуля NumPy)

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

Могу ли я настроить поведение?

Есть некоторый аспект ==, который не был упомянут в других ответах: это часть Python, «Модель данных» . Это означает, что его поведение можно настроить с помощью __eq__метода. Например:

class MyClass(object):
    def __init__(self, val):
        self._value = val

    def __eq__(self, other):
        print('__eq__ method called')
        try:
            return self._value == other._value
        except AttributeError:
            raise TypeError('Cannot compare {0} to objects of type {1}'
                            .format(type(self), type(other)))

Это всего лишь искусственный пример, иллюстрирующий, что метод действительно называется:

>>> MyClass(10) == MyClass(10)
__eq__ method called
True

Обратите внимание, что по умолчанию (если никакая другая реализация не __eq__может быть найдена в классе или суперклассах) __eq__использует is:

class AClass(object):
    def __init__(self, value):
        self._value = value

>>> a = AClass(10)
>>> b = AClass(10)
>>> a == b
False
>>> a == a

Так что на самом деле важно реализовать, __eq__если вы хотите «больше», чем просто сравнение ссылок для пользовательских классов!

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

Будут ли эти сравнения всегда возвращать логическое значение?

Поскольку __eq__может быть повторно реализован или переопределен, он не ограничивается возвратом Trueили False. Он может вернуть все что угодно (но в большинстве случаев он должен возвращать логическое значение!).

Например, для массивов NumPy ==будет возвращен массив:

>>> import numpy as np
>>> np.arange(10) == 2
array([False, False,  True, False, False, False, False, False, False, False], dtype=bool)

Но isчеки всегда будут возвращаться Trueили False!


1 Как отметил Аарон Холл в комментариях:

Как правило, вы не должны делать никаких проверок is Trueили is Falseпроверок, потому что обычно эти «проверки» используются в контексте, который неявно преобразует условие в логическое значение (например, в ifоператоре). Таким образом, is Trueсравнение и неявное логическое приведение делают больше работы, чем простое приведение - и вы ограничиваетесь булевыми значениями (которые не считаются питоническими).

Как PEP8 упоминает:

Не сравнивайте логические значения с Trueили Falseиспользуя ==.

Yes:   if greeting:
No:    if greeting == True:
Worse: if greeting is True:
MSeifert
источник
2
Я собираюсь не согласиться с вашим утверждением, чтобы сравнить «константы» с isименами, которые указывают на логические значения, следует проверять с помощью логического контекста, например if __debug__:или if not __debug__:. Вы никогда не должны делать if __debug__ is True:или, if __debug__ == True:кроме того, константа - это просто постоянное семантическое значение, а не единичное, поэтому проверка isв этом случае не является семантически правильной. Я призываю вас найти источник в поддержку ваших утверждений - я не думаю, что вы найдете его.
Аарон Холл
@AaronHall Что заставляет вас думать, что константы не являются синглетонами? Обратите внимание , что только None, True, Falseи __debug__то , что вы могли бы назвать «постоянное семантическое значение», поскольку они не могут быть переназначены. Но все они синглтоны.
Майферт
Прочтите PEP 8 - Ctrl-F и найдите слово «хуже». - Если вы проводите юнит тестирование, вы бы использовали self.assertTrue
Аарон Холл
@AaronHall В некоторых случаях вам действительно нужна проверка is Trueили if Falseпроверка (но да, это довольно редко - но если вы сделаете их, вы можете сделать их, используя is). Вот почему даже CPython использует их иногда (например, здесь или здесь )
MSeifert
19

Они совершенно разные . isпроверяет идентичность объекта, а ==проверяет равенство (понятие, которое зависит от типов двух операндов).

Это только счастливое совпадение, что " is", кажется, работает правильно с маленькими целыми числами (например, 5 == 4 + 1). Это потому, что CPython оптимизирует хранение целых чисел в диапазоне (от -5 до 256), делая их одиночными . Это поведение полностью зависит от реализации и не гарантируется, что оно будет сохранено при всех видах незначительных преобразовательных операций.

Например, Python 3.5 также делает короткие строки одиночными, но их нарезка нарушает это поведение:

>>> "foo" + "bar" == "foobar"
True
>>> "foo" + "bar" is "foobar"
True
>>> "foo"[:] + "bar" == "foobar"
True
>>> "foo"[:] + "bar" is "foobar"
False
Дэн Ленски
источник
10

https://docs.python.org/library/stdtypes.html#comparisons

isтесты на идентичность ==тесты на равенство

Каждое (маленькое) целочисленное значение отображается на одно значение, поэтому каждые 3 идентичны и равны. Это деталь реализации, а не часть языковой спецификации

mmaibaum
источник
6

Ваш ответ правильный. isОператор сравнивает идентичность двух объектов. ==Оператор сравнивает значения двух объектов.

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

Вы можете управлять поведением сравнения значений объекта, определяя __cmp__метод или расширенный метод сравнения, например __eq__.

Дэйв Уэбб
источник
3

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

a=[1,2,3]
b=a        #a and b point to the same object
c=list(a)  #c points to different object 

if a==b:
    print('#')   #output:#
if a is b:
    print('##')  #output:## 
if a==c:
    print('###') #output:## 
if a is c:
    print('####') #no output as c and a point to different object 
suvojit_007
источник
2

Как сказал Джон Феминелла, большую часть времени вы будете использовать == и! =, Потому что ваша цель - сравнить значения. Я просто хотел бы классифицировать то, что вы будете делать в остальное время:

Существует один и только один экземпляр NoneType, т. Е. None является одиночным. Следовательно foo == Noneи foo is Noneзначит то же самое. Тем не менее, isтест быстрее и Pythonic соглашение использовать foo is None.

Если вы проводите некоторый самоанализ или разбираетесь со сборкой мусора или проверяете, работает ли ваш пользовательский встроенный гаджет для интернирования строк или что-то подобное, то у вас, вероятно, есть вариант использования для foois bar.

Истина и Ложь также (в настоящее время) синглтоны, но нет ни одного варианта использования для, foo == Trueни никакого варианта использования для foo is True.

Джон Мачин
источник
2

Большинство из них уже ответили на вопрос. Как дополнительная заметка (основываясь на моем понимании и экспериментах, но не на документированном источнике), заявление

== если объекты, на которые ссылаются переменные, равны

Сверху ответы следует читать как

== если объекты, на которые ссылаются переменные, равны и объекты принадлежат к одному типу / классу

, Я пришел к такому выводу на основе теста ниже:

list1 = [1,2,3,4]
tuple1 = (1,2,3,4)

print(list1)
print(tuple1)
print(id(list1))
print(id(tuple1))

print(list1 == tuple1)
print(list1 is tuple1)

Здесь содержимое списка и кортежа одинаковы, но тип / класс различны.

Sandeep
источник
2

Разница в Python между is и equals (==)

Оператор is может выглядеть так же, как оператор равенства, но они не совпадают.

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

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

Вот пример, демонстрирующий сходство и различие.

>>> a = b = [1,2,3]
>>> c = [1,2,3]
>>> a == b
True
>>> a == c
True
>>> a is b
True
>>> a is c
False
>>> a = [1,2,3]
>>> b = [1,2]
>>> a == b
False
>>> a is b
False
>>> del a[2]
>>> a == b
True
>>> a is b
False
Tip: Avoid using is operator for immutable types such as strings and numbers, the result is unpredictable.
Проеш Бхумик
источник
1
Пожалуйста, используйте кавычки только для текста, который вы цитировали из другого источника, после чего вы должны включить атрибуцию (см. Stackoverflow.com/help/referencing ). Если это ваш собственный текст, удалите кавычки блока.
Мартин Питерс
0

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

Для сравнения строк обязательно используйте ==вместо is:

str = 'hello'
if (str is 'hello'):
    print ('str is hello')
if (str == 'hello'):
    print ('str == hello')

Вне:

str is hello
str == hello

Но в приведенном ниже примере ==и isполучим разные результаты:

str = 'hello sam'
    if (str is 'hello sam'):
        print ('str is hello sam')
    if (str == 'hello sam'):
        print ('str == hello sam')

Вне:

str == hello sam

Вывод:

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

imanzabet
источник
почему "is" "работает так для строк с пробелами?
Акаш Гупта
Согласно предыдущим ответам: кажется, что python выполняет кеширование с маленьким целым числом и строками, что означает, что он использует одну и ту же ссылку на объект для появления строк 'hello' в этом снимке кода, в то время как он не преформировал кэширование для 'hello sam', как это относительно больше, чем 'hello' (т.е. он управляет различными ссылками на строку 'hello sam', и поэтому оператор 'is' возвращает false в следующем примере) Пожалуйста, исправьте меня, если я ошибаюсь
Рида Шамасне