Heim  >  Artikel  >  Backend-Entwicklung  >  is und id in Python

is und id in Python

高洛峰
高洛峰Original
2016-10-18 10:06:381134Durchsuche

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

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

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


Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn