Python! = Operation vs "ist nicht"

250

In einem Kommentar zu dieser Frage sah ich eine Aussage, die die Verwendung empfahl

result is not None

vs.

result != None

Ich habe mich gefragt, was der Unterschied ist und warum einer dem anderen empfohlen werden könnte.

viksit
quelle
1
Hmm. Während die Antwort auf beide Fragen dasselbe Konzept ist, denke ich, dass die positiven Stimmen und detaillierten Antworten hier unabhängig voneinander zum Konzept der Identitäts- und Gleichheitsprüfung beitragen.
Viksit

Antworten:

301

==ist ein Gleichheitstest . Es wird geprüft, ob die rechte und die linke Seite gleiche Objekte sind (entsprechend ihrer __eq__oder ihrer __cmp__Methoden).

isist ein Identitätstest . Es wird geprüft, ob die rechte und die linke Seite dasselbe Objekt sind. Es werden keine Methodenaufrufe durchgeführt, Objekte können den isVorgang nicht beeinflussen .

Sie verwenden is(und is not) für Singletons, z. B. Nonewenn Sie sich nicht für Objekte interessieren, die dies vorgeben möchten, Noneoder wenn Sie sich vor Objekten schützen möchten, die beim Vergleich mit Objekten brechen None.

Thomas Wouters
quelle
3
Vielen Dank für die Antwort. Können Sie Situationen erläutern, in denen ein Objekt brechen kann, wenn Sie es mit None vergleichen?
Viksit
3
@ Viksit. Nonehat wenige Methoden und fast keine Attribute. Wenn Ihr __eq__Test eine Methode oder ein Attribut erwartet hat, ist diese möglicherweise fehlerhaft. def __eq__( self, other ): return self.size == other.size. Zum Beispiel wird brechen, wenn otherzufällig ist None.
S.Lott
36
Meine Lieblingsmethode, um dies zu verstehen, ist: Pythons isist wie Javas ==. Pythons ==ist wie Javas .equals(). Dies hilft natürlich nur, wenn Sie Java kennen.
MatrixFrog
4
@MatrixFrog: In PHP oder JavaScript würden wir sagen, dass dies iswie ===(sehr gleich) und umgekehrt is notwie !==(nicht genau gleich) ist.
Orwellophile
3
Ist is notein einzelner Operator oder negiert er nur das Ergebnis von isintern wie not foo is bar?
Asad Moosvi
150

Lassen Sie mich zunächst einige Begriffe durchgehen. Wenn Sie nur Ihre Frage beantworten möchten, scrollen Sie nach unten zu "Beantworten Ihrer Frage".

Definitionen

Objektidentität : Wenn Sie ein Objekt erstellen, können Sie es zu einer Variablen zuweisen können. Sie können es dann auch einer anderen Variablen zuweisen. Und ein anderer.

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

In diesem Fall cancel, closeund dismissbeziehen sich alle auf das gleiche Objekt im Speicher. Sie haben nur ein ButtonObjekt erstellt, und alle drei Variablen beziehen sich auf dieses eine Objekt. Wir sagen das cancel, closeund dismissalle beziehen sich auf identische Objekte; Das heißt, sie beziehen sich auf ein einzelnes Objekt.

Objektgleichheit : Wenn Sie zwei Objekte vergleichen, ist es Ihnen normalerweise egal, dass sie sich auf genau dasselbe Objekt im Speicher beziehen . Mit der Objektgleichheit können Sie Ihre eigenen Regeln für den Vergleich zweier Objekte definieren. Wenn Sie schreiben if a == b:, sagen Sie im Wesentlichen if a.__eq__(b):. Auf diese Weise können Sie eine __eq__Methode definieren a, mit der Sie Ihre eigene Vergleichslogik verwenden können.

Begründung für Gleichstellungsvergleiche

Begründung: Zwei Objekte haben genau die gleichen Daten, sind jedoch nicht identisch. (Sie sind nicht dasselbe Objekt im Speicher.) Beispiel: Strings

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

Hinweis: Ich verwende hier Unicode-Zeichenfolgen, da Python intelligent genug ist, um reguläre Zeichenfolgen wiederzuverwenden, ohne neue im Speicher zu erstellen.

Hier habe ich zwei Unicode-Strings aund b. Sie haben genau den gleichen Inhalt, aber sie sind nicht das gleiche Objekt im Speicher. Wenn wir sie jedoch vergleichen, möchten wir, dass sie gleich sind. Was hier passiert ist, dass das Unicode-Objekt die __eq__Methode implementiert hat .

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

Hinweis: __eq__on unicodeist definitiv effizienter implementiert.

Begründung: Zwei Objekte haben unterschiedliche Daten, werden jedoch als dasselbe Objekt betrachtet, wenn einige Schlüsseldaten identisch sind. Beispiel: Die meisten Arten von Modelldaten

>>> 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

Hier habe ich zwei Dell-Monitore aund b. Sie haben die gleiche Marke und das gleiche Modell. Sie haben jedoch weder dieselben Daten noch dasselbe Objekt im Speicher. Wenn wir sie jedoch vergleichen, möchten wir, dass sie gleich sind. Was hier passiert, ist, dass das Monitor-Objekt die __eq__Methode implementiert hat .

class Monitor(object):
    # ...

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

Beantwortung Ihrer Frage

NoneVerwenden Sie im Vergleich zu immer is not. Keiner ist ein Singleton in Python - es gibt immer nur eine Instanz davon im Speicher.

Durch den Vergleich der Identität kann dies sehr schnell durchgeführt werden. Python prüft, ob das Objekt, auf das Sie sich beziehen, dieselbe Speicheradresse wie das globale None-Objekt hat - ein sehr, sehr schneller Vergleich zweier Zahlen.

Durch den Vergleich der Gleichheit muss Python nachschlagen, ob Ihr Objekt über eine __eq__Methode verfügt. Ist dies nicht der Fall, wird jede Oberklasse auf der Suche nach einer __eq__Methode untersucht. Wenn es eines findet, ruft Python es auf. Dies ist besonders schlimm, wenn die __eq__Methode langsam ist und nicht sofort zurückkehrt, wenn sie bemerkt, dass das andere Objekt ist None.

Haben Sie nicht implementiert __eq__? Dann wird Python wahrscheinlich die __eq__Methode finden objectund diese stattdessen verwenden - die ohnehin nur die Objektidentität überprüft.

Wenn Sie die meisten anderen Dinge in Python vergleichen, werden Sie verwenden !=.

Wesley
quelle
42

Folgendes berücksichtigen:

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)
Alok Singhal
quelle
1
Dies ist ein sehr hilfreiches und einfaches Beispiel. Danke dir.
Msarafzadeh
18

Noneist ein Singleton, daher funktioniert der Identitätsvergleich immer, während ein Objekt den Gleichheitsvergleich über vortäuschen kann .__eq__().

Ignacio Vazquez-Abrams
quelle
Ah interessant! In welchen Situationen könnte man übrigens den Gleichheitsvergleich vortäuschen wollen? Ich vermute, dies hat in gewisser Weise Auswirkungen auf die Sicherheit.
Viksit
1
Es geht nicht darum, Gleichheit vorzutäuschen, es geht darum , Gleichheit umzusetzen . Es gibt viele Gründe, um zu definieren, wie ein Objekt mit einem anderen verglichen wird.
Thomas Wouters
1
Ich würde sagen, es sind mehr Auswirkungen auf die Verwirrung als auf die Sicherheit.
Greg Hewgill
2
Ich bin nicht auf einen Grund gestoßen, Gleichheit gegen zu fälschen None, aber falsches Verhalten in Bezug auf Nonekönnte als Nebeneffekt der Implementierung von Gleichheit gegen andere Typen auftreten. Es sind weniger Auswirkungen auf die Sicherheit als vielmehr Auswirkungen auf die Korrektheit.
Ignacio Vazquez-Abrams
Ah so, ich verstehe. Danke für die Klarstellung.
Viksit
10
>>> () ist ()
Wahr
>>> 1 ist 1
Wahr
>>> (1,) == (1,)
Wahr
>>> (1,) ist (1,)
Falsch
>>> a = (1,)
>>> b = a
>>> a ist b
Wahr

Einige Objekte sind Singletons und entsprechen daher ismit ihnen ==. Die meisten sind es nicht.

kurzlebig
quelle
4
Die meisten davon funktionieren nur durch Zufall / Implementierungsdetails. ()und 1sind nicht von Natur aus Singletons.
Mike Graham
1
In der CPython Implementierung sind die kleinen Zahlen ( -NSMALLNEGINTS <= n <= NSMALLPOSINTS) und leere Tupel sind Singletons. Es ist zwar weder dokumentiert noch garantiert, aber es ist unwahrscheinlich, dass es sich ändert.
Ephemient
3
Es ist so, wie es implementiert wird, aber es ist nicht sinnvoll oder nützlich oder lehrreich.
Mike Graham
1
Insbesondere ist CPython nicht die einzige Python-Implementierung. Sich auf Verhaltensweisen zu verlassen, die je nach Python-Implementierung variieren können, scheint mir im Allgemeinen eine schlechte Idee zu sein.
me_and