Python! = Операція проти "не"


250

У коментарі до цього питання я побачив твердження, яке рекомендував використовувати

result is not None

проти

result != None

Мені було цікаво, в чому різниця, і чому одне може бути рекомендовано над іншим?



1
Хм. Хоча відповідь на обидва запитання - це одне і те ж поняття, я думаю, що тут підсумки та детальні відповіді незалежно сприяють концепції тестування ідентичності та рівності.
viksit

Відповіді:


301

==є тестом на рівність . Він перевіряє , права і ліва сторона , чи є рівноправними об'єктами ( в залежності від їх __eq__або __cmp__методів) .

is- це тест на ідентичність . Він перевіряє, чи права, так і ліва - це той самий об’єкт. Жодні виклики методів не виконуються, об'єкти не можуть впливати на isоперацію.

Ви використовуєте isis not) для одиночних клавіш, наприклад None, там, де вам не байдуже об’єкти, які можуть хотіти робити вигляд, Noneабо де ви хочете захистити від обривів об'єктів у порівнянні з None.


3
Дякуємо за відповідь - чи могли б ви детальніше розібратися в ситуаціях, коли об’єкт може зламатися, порівнюючи його з None
viksit

3
@viksit. Noneмає мало методів і майже немає атрибутів. Якщо ваш __eq__тест очікував метод або атрибут, він може зламатися. def __eq__( self, other ): return self.size == other.size. Наприклад, зламається, якщо otherтрапиться None.
S.Lott

36
Мій улюблений спосіб зрозуміти це: Python - isце як у Java ==. Python - ==це як у Java .equals(). Звичайно, це допомагає лише якщо ви знаєте Java.
MatrixFrog

4
@MatrixFrog: У PHP або JavaScript ми би сказали, що isце як ===(дуже рівний), і навпаки is not- це як !==(не зовсім рівно).
Орвелофіл

3
Це is notєдиний оператор або це просто заперечення результату isвнутрішньо подібного not foo is bar?
Асад Мосві

150

По-перше, дозвольте мені перейти кілька термінів. Якщо ви просто хочете, щоб ваше запитання відповіло, прокрутіть униз до пункту "Відповідь на ваше запитання".

Визначення

Ідентифікація об'єкта : Коли ви створюєте об'єкт, ви можете призначити його змінної. Потім ви можете також призначити його іншій змінній. І ще.

>>> button = Button()
>>> cancel = button
>>> close = button
>>> dismiss = button
>>> print(cancel is close)
True

В цьому випадку cancel, closeі dismissвсе вони відносяться до одного об'єкту в пам'яті. Ви створили лише один Buttonоб'єкт, і всі три змінні стосуються цього одного об’єкта. Ми говоримо, що cancel, closeі dismissвсі відносяться до однакових об'єктів; тобто вони посилаються на один єдиний об’єкт.

Рівність об'єкта : Якщо порівняти два об'єкти, то , як правило , не хвилює , що він відноситься до точному і тому ж об'єкту в пам'яті. За допомогою рівності об'єктів ви можете визначити власні правила порівняння двох об'єктів. Коли ти пишеш if a == b:, ти по суті кажеш if a.__eq__(b):. Це дозволяє вам визначити __eq__метод aтак, щоб ви могли використовувати власну логіку порівняння.

Обґрунтування порівняння рівності

Обґрунтування: Два об’єкти мають точно однакові дані, але не є тотожними. (У пам’яті вони не є одним і тим же об’єктом.) Приклад: Струни

>>> greeting = "It's a beautiful day in the neighbourhood."
>>> a = unicode(greeting)
>>> b = unicode(greeting)
>>> a is b
False
>>> a == b
True

Примітка. Тут я використовую рядки Unicode, оскільки Python досить розумний для повторного використання звичайних рядків без створення нових в пам'яті.

Тут у мене є два рядки unicode, aі b. Вони мають точно однаковий зміст, але вони не є тим самим об’єктом у пам’яті. Однак, коли ми порівнюємо їх, ми хочемо, щоб вони порівняли рівні. Тут відбувається те, що об’єкт unicode реалізував __eq__метод.

class unicode(object):
    # ...

    def __eq__(self, other):
        if len(self) != len(other):
            return False

        for i, j in zip(self, other):
            if i != j:
                return False

        return True

Примітка: функція __eq__on unicode, безумовно, реалізується більш ефективно, ніж це.

Обґрунтування: Два об'єкти мають різні дані, але вважаються одним і тим же об'єктом, якщо деякі ключові дані однакові. Приклад: Більшість типів даних моделі

>>> import datetime
>>> a = Monitor()
>>> a.make = "Dell"
>>> a.model = "E770s"
>>> a.owner = "Bob Jones"
>>> a.warranty_expiration = datetime.date(2030, 12, 31)
>>> b = Monitor()
>>> b.make = "Dell"
>>> b.model = "E770s"
>>> b.owner = "Sam Johnson"
>>> b.warranty_expiration = datetime.date(2005, 8, 22)
>>> a is b
False
>>> a == b
True

Тут у мене є два монітори Dell, aі b. Вони мають однакову марку і модель. Однак вони ні мають однакових даних, ні є однаковим об'єктом у пам'яті. Однак, коли ми порівнюємо їх, ми хочемо, щоб вони порівняли рівні. Тут відбувається те, що об'єкт "Монітор" реалізував __eq__метод.

class Monitor(object):
    # ...

    def __eq__(self, other):
        return self.make == other.make and self.model == other.model

Відповідаючи на ваше запитання

Для порівняння Noneзавжди використовуйте is not. У Python жоден не є одинаком - в пам'яті є лише один примірник цього.

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

Порівнюючи рівність , Python повинен шукати, чи є у вашого об'єкта __eq__метод. Якщо цього немає, він вивчає кожен суперклас, шукаючи __eq__метод. Якщо він знайде його, Python викликає його. Це особливо погано, якщо __eq__метод повільний і не одразу повертається, коли помічає, що є іншим об'єктом None.

Ви не реалізували __eq__? Тоді Python, ймовірно, знайде __eq__метод objectі буде замість цього використовувати, який просто перевіряє ідентичність об'єкта.

Порівнюючи більшість інших речей у Python, ви будете використовувати !=.


42

Розглянемо наступне:

class Bad(object):
    def __eq__(self, other):
        return True

c = Bad()
c is None # False, equivalent to id(c) == id(None)
c == None # True, equivalent to c.__eq__(None)

1
Це дуже корисний і простий приклад. Дякую.
msarafzadeh

18

Noneє одинарним, тому порівняння ідентичності завжди працюватиме, тоді як об'єкт може підробити порівняння рівності через .__eq__().


Ах цікаво! У яких ситуаціях можна спробувати підробити порівняння рівності btw? Я здогадуюсь, це має певний вплив на безпеку.
viksit

1
Йдеться не про підробку рівності, а про реалізацію рівності. Існує маса причин, які хочуть визначити, як об’єкт порівнюється з іншим.
Thomas Wouters

1
Я б сказав, що це більше заплутаних наслідків, ніж наслідків для безпеки.
Грег Хьюгілл

2
Я не підходив проти причини підробки рівності проти None, але неправильна поведінка щодо цього Noneмогла виникнути як побічний ефект від реалізації рівності проти інших типів. Це не стільки наслідки для безпеки, скільки просто правильність.
Ігнасіо Васкес-Абрамс

Ах так, я бачу. Thx для уточнення.
viksit

10
>>> () є ()
Правда
>>> 1 - це 1
Правда
>>> (1,) == (1,)
Правда
>>> (1,) є (1,)
помилковий
>>> a = (1,)
>>> b = a
>>> а - б
Правда

Деякі об'єкти є одинаковими, і, таким чином, isз ними рівносильно ==. Більшість - ні.


4
Більшість із них працюють лише за збігом обставин / деталізацією реалізації. ()і 1по суті не є однотонними.
Майк Грехем

1
У реалізації CPython малі цілі числа ( -NSMALLNEGINTS <= n <= NSMALLPOSINTS) та порожні кортежі є одинаковими. Дійсно, це не зафіксовано і не гарантується, але навряд чи це зміниться.
ефемієнт

3
Це як воно реалізоване, але це не має сенсу, корисного чи навчального.
Майк Грем

1
І зокрема, CPython - не єдина реалізація Python. Покладатися на поведінку, яка може відрізнятися в різних реалізаціях Python, здавалося б, як правило, для мене погана ідея ™.
me_and
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.