Чи є різниця між "==" і "є"?


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 - foooutput : False True False.
ahuigo

Ви втратили мене з 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**3правда це чи ні , залежить від реалізації та залежить від компілятора, який попередньо оцінює вираз 10**3. x=10; 1000 is x**3оцінює до False.
чепнер

312

Існує просте правило, яке підкаже вам, коли використовувати ==або 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 як одиночні екземпляри з міркувань продуктивності".
Джон Фемінелла


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 є y означає x == y).

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

Порівняння рівності повинно бути рефлексивним. Іншими словами, ідентичні об'єкти повинні порівнювати:

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 Інший виняток: Очевидно, офіційна документація рекомендує використовувати isдля порівняння Enums.
Артур

@Arthur Я додав список випадків використання ...
Aaron Hall

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)

У кожному іншому випадку ви повинні використовувати== для перевірки рівності.

Чи можна налаштувати поведінку?

Існує певний аспект, ==який уже не згадувався в інших відповідях: Це частина пітонів "Модель даних" . Це означає, що його поведінку можна налаштувати за допомогою __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:

2
Мені доведеться не погодитися з вашим твердженням, щоб порівнювати "константи" з is- імена, які вказують на булеві, слід перевіряти з булевим контекстом - як if __debug__:або if not __debug__:. Ніколи не слід робити if __debug__ is True:або if __debug__ == True:- далі, константа - це лише постійне смислове значення, а не сингтон, тому перевірка з isцим випадком не є семантично правильною. Я закликаю вас знайти джерело, яке б підтримало ваші твердження - я не думаю, що ви знайдете його.
Аарон Холл

@AaronHall Що змушує вас думати, що константи не є однотонними? Зверніть увагу , що тільки None, True, Falseі __debug__то , що ви могли б назвати «постійне семантичне значення», оскільки вони не можуть бути змінені. Але всі вони одинаки.
MSeifert

Прочитайте 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 є однаковим і рівним. Це детальна інформація про впровадження, але не є частиною мовної специфікації


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 

2

Як сказав Джон Фемінелла, більшу частину часу ви будете використовувати == і! = Тому що ваша мета - порівняння значень. Я просто хотів би класифікувати, чим би ви займалися решту часу:

Існує один і лише один екземпляр NoneType, тобто None є однократним. Отже foo == Noneі foo is Noneозначають те саме. Однак isтест швидше, і застосовуватиметься пітонічна умова foo is None.

Якщо ви робите деякий самоаналіз або відведення о с збирача сміттям або перевіркою інтернування чи ваша Несерійні рядок гаджет працюють або тому подібні, то ви , ймовірно , маєте споживчий випадок для fooце bar.

True та False також є (зараз) сингтонами, але для цього немає випадків 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)

Тут вміст списку та кортежу однаковий, але тип / клас відрізняються.


2

Різниця Python між є і дорівнює (==)

Оператор 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 ). Якщо це ваш власний текст, видаліть цитати блоків.
Martijn Pieters

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Ретельно використовуйте для порівняння між рядками


чому "" "працює так для рядків з пробілами?
Акаш Гупта,

Згідно з попередніми відповідями: Схоже, python виконує кешування на малі цілі числа та рядки, а це означає, що він використовує ту саму посилання на об'єкт для подій рядків 'привіт' у цьому знімку коду, хоча він не попередньо керував кешування для 'hello sam' як це є порівняно більший, ніж "привіт" (тобто він керує різними посиланнями на рядок "hello sam", і тому оператор "є" повертає помилку в наступному прикладі). Будь ласка, виправте мене, якщо я помиляюся
Rida Shamasneh
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.