Heim > Artikel > Backend-Entwicklung > is und id in Python
(ob1 ist ob2) ist äquivalent zu (id(ob1) == id(ob2))
Erstens kann die ID-Funktion die Speicheradresse des Objekts abrufen, wenn die Speicheradressen der beiden Objekte sind gleich. Dann müssen diese beiden Objekte ein Objekt sein. ist äquivalent zu ist. Python-Quellcode als Beweis.
static PyObject * cmp_outcome(int op, register PyObject *v, register PyObject *w) { int res = 0; switch (op) { case PyCmp_IS: res = (v == w); break; case PyCmp_IS_NOT: res = (v != w); break;
Aber sehen Sie bitte im folgenden Code, wie diese Situation auftritt?
In [1]: def bar(self, x):
...: return self.x y
...:
In [2]: Klasse Foo(Objekt):
...: x = 9
...: def __init__(self,x):
... : self.x = x
...: bar = bar
...: 🎜>In [4]: foo.bar ist Foo.bar
Out[4]: False
In [5]: id(foo.bar) == id(Foo.bar )
Out[5]: True
Zwei Objekte werden aufgrund ihrer Identität als falsch beurteilt. Dies steht im Widerspruch zu den uns bekannten Fakten. Was ist die Erklärung? Die beste Lösung für diese Situation besteht darin, das Modul dis aufzurufen, um zu sehen, was die beiden Vergleichsanweisungen bewirken.
In [7]: dis.dis("id(foo.bar) == id(Foo.bar)")
0 BUILD_MAP 10340
3 BUILD_TUPLE 28527
6
10 STORE_SLICE 1 11 SLICE 212 DELETE_SUBSCR
13 DELETE_SUBSCR
14 SLICE 2
15 BUILD_MAP 10340
18 PRINT_EXPR
19 JUMP_IF_FALSE_OR_POP 11887
22 DELETE_GLOBAL 29281 (29281)
25 STORE_ SCHEIBE 1
In [8]: dis.dis("foo.bar is Foo.bar")
0 BUILD_TUPLE 28527
3
4 DELETE_GLOBAL 29281 (29281) 7 SLICE 2 8 BUILD_MAP 8307 11 PRINT_EXPR12 JUMP_IF_FALSE_OR_POP 11887
15 DELETE_GLOBAL 29281. (29 281)
Die tatsächliche Situation ist, dass bei der Ausführung des .-Operators tatsächlich ein Proxy-Objekt generiert wird. Bei Foo.bar werden zwei Objekte nacheinander generiert und auf dem Stapel verglichen Muss falsch sein, aber es ist anders, wenn id(foo.bar) == id(Foo.bar) zuerst .bar generiert wird und dann die Adresse von foo.bar berechnet wird , es gibt kein Objekt, das auf foo.bar zeigt, daher wird das foo.bar-Objekt freigegeben. Generieren Sie dann das Foo.bar-Objekt. Da foo.bar und Foo.bar dieselbe Speichergröße belegen, wird die Speicheradresse der ursprünglichen foo.bar wiederverwendet, also id(foo.bar) == id(Foo. bar ) ist wahr.
Der folgende Inhalt wird von Leo Jay per E-Mail bereitgestellt. Er erklärt ihn klarer.
Die Idee, id(expression a) == id(expression b) zu verwenden, um zu bestimmen, ob die Ergebnisse zweier Ausdrücke dasselbe Objekt sind, ist problematisch.
foo.bar Dieses Formular wird als Attributreferenz [1] bezeichnet und ist eine Art Ausdruck. foo ist ein Instanzobjekt und bar ist eine Methode. Zu diesem Zeitpunkt wird das vom Ausdruck foo.bar zurückgegebene Ergebnis als Methodenobjekt [2] bezeichnet. Laut Dokumentation:
Wenn auf ein Instanzattribut verwiesen wird, das kein Datenattribut ist,
wird nach seiner Klasse gesucht, wenn der Name ein gültiges Klassenattribut angibt
Das ist ein Funktionsobjekt. Ein Methodenobjekt wird erstellt, indem
(Zeiger auf) das Instanzobjekt und das gerade gefundene Funktionsobjekt
zusammen in ein abstraktes Objekt gepackt werden: Dies ist das Methodenobjekt.
foo.bar selbst ist kein einfacher Name, sondern das Berechnungsergebnis eines Ausdrucks, der ein Methodenobjekt ist. In einem Ausdruck wie id(foo.bar) ist das Methodenobjekt nur ein temporäres Zwischenprodukt Variable. Es macht keinen Sinn, temporäre Zwischenvariablen zu kennzeichnen.
Ein offensichtlicheres Beispiel ist:
print id(foo.bar) == id(foo.__init__)
Das Ausgabeergebnis ist ebenfalls True
Sehen Sie sich die Dokumentation für id[3] an:
Gibt die „Identität“ eines Objekts zurück. Dies ist eine Ganzzahl (oder ein langer Wert
).Ganzzahl), die für dieses Objekt
während seiner Lebensdauer garantiert eindeutig und konstant ist. Zwei Objekte mit nicht überlappenden Lebensdauern können
den gleichen id()-Wert haben
Details zur CPython-Implementierung: Dies ist die Adresse des Objekts im Speicher.
Nur wenn Sie garantieren können, dass das Objekt nicht zerstört wird, können Sie die ID zum Vergleichen zweier Objekte verwenden. Wenn Sie also vergleichen müssen, müssen Sie so schreiben:
fb = foo.bar
Fb = Foo.bar
print id(fb) == id( Fb)
Das heißt, binden Sie die Ergebnisse der beiden Ausdrücke an die Namen und vergleichen Sie dann, ob es sich um dasselbe Objekt handelt, damit Sie das richtige Ergebnis erhalten.
Der is-Ausdruck [4] ist derselbe. Sie erhalten jetzt das richtige Ergebnis, was ausschließlich auf die aktuellen Implementierungsdetails von CPython zurückzuführen ist. Die aktuelle Implementierung besteht darin, die Objekte auf der linken und rechten Seite zu berechnen und dann zu vergleichen, ob die Adressen der beiden Objekte gleich sind. Wenn es sich eines Tages ändert, berechnen Sie zuerst die linke Seite, speichern Sie die Adresse, geben Sie die linke Seite frei, berechnen Sie dann die rechte Seite und vergleichen Sie erneut. Das Ergebnis Ihrer Berechnung ist möglicherweise falsch. Dieses Problem wird auch in der offiziellen Dokumentation erwähnt [5]. Ich denke, die richtige Methode ist wie bei id. Berechnen Sie zuerst sowohl die linke als auch die rechte Seite und binden Sie sie explizit an ihren jeweiligen Namen, und verwenden Sie sie dann zur Beurteilung.