Wofür wird die Funktion id () verwendet?

99

Ich habe die Python 2-Dokumente gelesen und die id()Funktion bemerkt :

Gibt die "Identität" eines Objekts zurück. Dies ist eine Ganzzahl (oder lange Ganzzahl), die für dieses Objekt während seiner Lebensdauer garantiert eindeutig und konstant ist. Zwei Objekte mit nicht überlappenden Lebensdauern können denselben id () -Wert haben.

CPython-Implementierungsdetail: Dies ist die Adresse des Objekts im Speicher.

Also habe ich id()mit einer Liste experimentiert :

>>> list = [1,2,3]
>>> id(list[0])
31186196
>>> id(list[1])
31907092 // increased by 896
>>> id(list[2])
31907080 // decreased by 12

Was ist die von der Funktion zurückgegebene Ganzzahl? Ist es auch für Speicheradressen in C? Wenn ja, warum entspricht die Ganzzahl nicht der Größe des Datentyps?

Wann wird id()in der Praxis angewendet?

Thanakron Tandavas
quelle
4
Nur weil Sie ein 32-Bit-Int in einer Datenstruktur in einer Skriptsprache speichern (sagen wir), bedeutet dies nicht, dass Sie 32 Bit mehr Speicher verbrauchen. Es sind IMMER Metadaten an JEDE von Ihnen gespeicherten Daten angehängt. Typ, Größe, Länge, bla bla bla.
Marc B
cpython wird von einem Heap zugewiesen, der verschlüsselt wird, wenn Objekte malloc'd und free'd sind.
tdelaney
Python-Zahlen sind keine einfachen Daten. Hierbei handelt es sich um Objekte, die zunächst intern Longs verwenden und dann automatisch zu einer Darstellung im BigNumber-Stil heraufstufen, wenn der Wert zu groß wird.
Gareth Latty

Antworten:

156

Ihr Beitrag stellt mehrere Fragen:

Wie lautet die von der Funktion zurückgegebene Nummer?

Es ist " eine Ganzzahl (oder lange Ganzzahl), die für dieses Objekt während seiner Lebensdauer garantiert eindeutig und konstant ist. " (Python Standard Library - Integrierte Funktionen) Eine eindeutige Zahl. Nicht mehr und nicht weniger. Stellen Sie sich das als Sozialversicherungsnummer oder Mitarbeiter-ID für Python-Objekte vor.

Ist es dasselbe mit Speicheradressen in C?

Konzeptionell ja, da beide zu Lebzeiten garantiert einzigartig in ihrem Universum sind. In einer bestimmten Implementierung von Python ist dies tatsächlich die Speicheradresse des entsprechenden C-Objekts.

Wenn ja, warum erhöht sich die Anzahl nicht sofort um die Größe des Datentyps (ich gehe davon aus, dass es int wäre)?

Weil eine Liste kein Array ist und ein Listenelement eine Referenz, kein Objekt.

Wann nutzen wir die id( )Funktion wirklich ?

Fast nie. id()(oder gleichwertig) wird im isOperator verwendet.

Robᵩ
quelle
Wir verwenden id () normalerweise zu Demonstrationszwecken wie hier stackoverflow.com/questions/17246693/…
Nabin
49

Das ist die Identität des Ortes des Objekts im Speicher ...

Dieses Beispiel könnte Ihnen helfen, das Konzept ein wenig besser zu verstehen.

foo = 1
bar = foo
baz = bar
fii = 1

print id(foo)
print id(bar)
print id(baz)
print id(fii)

> 1532352
> 1532352
> 1532352
> 1532352

Diese zeigen alle auf dieselbe Stelle im Speicher, weshalb ihre Werte gleich sind. Im Beispiel 1wird nur einmal gespeichert, und alles andere, auf 1das verwiesen wird, verweist auf diesen Speicherort.

brbcoding
quelle
9
Wenn Sie jedoch Zahlen außerhalb des Bereichs von -5 bis 256 verwenden, erhalten Sie nicht dieselbe ID für die Variable fii.
Saurav
das ist extrem interessant. Kannst du mehr teilen?
Jouell
3
Ich denke, diese Antwort ist irreführend, da dies für die meisten Zahlen nicht gilt. Der Operator "is" verhält sich unerwartet mit ganzen Zahlen .
Kevinji
8

id()Gibt zwar die Adresse des Objekts zurück, auf das verwiesen wird (in CPython), aber Ihre Verwirrung ergibt sich aus der Tatsache, dass sich Python-Listen stark von C-Arrays unterscheiden. In einer Python-Liste ist jedes Element eine Referenz . Was Sie also tun, ist diesem C-Code viel ähnlicher:

int *arr[3];
arr[0] = malloc(sizeof(int));
*arr[0] = 1;
arr[1] = malloc(sizeof(int));
*arr[1] = 2;
arr[2] = malloc(sizeof(int));
*arr[2] = 3;
printf("%p %p %p", arr[0], arr[1], arr[2]);

Mit anderen Worten, Sie drucken die Adresse aus der Referenz und nicht eine Adresse in Bezug auf den Speicherort Ihrer Liste.

In meinem Fall hat sich die id()Funktion als nützlich erwiesen, um undurchsichtige Handles zu erstellen, die beim Aufrufen pythonvon C zum C-Code zurückkehren . Auf diese Weise können Sie das Objekt mit einem Wörterbuch leicht von seinem Handle aus nachschlagen, und es ist garantiert eindeutig.

Fataler Fehler
quelle
7

Robs Antwort (die meisten stimmten oben) ist richtig. Ich möchte hinzufügen, dass die Verwendung von IDs in einigen Situationen nützlich ist, da hiermit Objekte verglichen und festgestellt werden können, welche Objekte auf Ihre Objekte verweisen.

Letzteres hilft Ihnen normalerweise beim Debuggen von seltsamen Fehlern, bei denen veränderbare Objekte als Parameter für Klassen übergeben und lokalen Variablen in einer Klasse zugewiesen werden. Durch das Mutieren dieser Objekte werden vars in einer Klasse mutiert. Dies äußert sich in einem seltsamen Verhalten, bei dem sich mehrere Dinge gleichzeitig ändern.

Vor kurzem hatte ich dieses Problem mit einer Python / Tkinter-App, bei der das Bearbeiten von Text in einem Texteingabefeld den Text in einem anderen während der Eingabe geändert hat :)

Hier ist ein Beispiel, wie Sie die Funktion id () verwenden können, um zu verfolgen, wo sich diese Referenzen befinden. Auf jeden Fall ist dies keine Lösung, die alle möglichen Fälle abdeckt, aber Sie haben die Idee. Wieder werden IDs im Hintergrund verwendet und der Benutzer sieht sie nicht:

class democlass:
    classvar = 24

    def __init__(self, var):
        self.instancevar1 = var
        self.instancevar2 = 42

    def whoreferencesmylocalvars(self, fromwhere):
        return {__l__: {__g__
                    for __g__ in fromwhere
                        if not callable(__g__) and id(eval(__g__)) == id(getattr(self,__l__))
                    }
                for __l__ in dir(self)
                    if not callable(getattr(self, __l__)) and __l__[-1] != '_'
                }

    def whoreferencesthisclassinstance(self, fromwhere):
        return {__g__
                    for __g__ in fromwhere
                        if not callable(__g__) and id(eval(__g__)) == id(self)
                }

a = [1,2,3,4]
b = a
c = b
democlassinstance = democlass(a)
d = democlassinstance
e = d
f = democlassinstance.classvar
g = democlassinstance.instancevar2

print( 'My class instance is of', type(democlassinstance), 'type.')
print( 'My instance vars are referenced by:', democlassinstance.whoreferencesmylocalvars(globals()) )
print( 'My class instance is referenced by:', democlassinstance.whoreferencesthisclassinstance(globals()) )

AUSGABE:

My class instance is of <class '__main__.democlass'> type.
My instance vars are referenced by: {'instancevar2': {'g'}, 'classvar': {'f'}, 'instancevar1': {'a', 'c', 'b'}}
My class instance is referenced by: {'e', 'd', 'democlassinstance'}

Unterstriche in Variablennamen werden verwendet, um Namenskollisionen zu verhindern. Funktionen verwenden das Argument "fromwhere", damit Sie ihnen mitteilen können, wo sie mit der Suche nach Referenzen beginnen sollen. Dieses Argument wird von einer Funktion gefüllt, die alle Namen in einem bestimmten Namespace auflistet. Globals () ist eine solche Funktion.

Arijan
quelle
Tolle Erklärung!
Neeraj Verma
5

Ich beginne mit Python und verwende id, wenn ich die interaktive Shell verwende, um zu sehen, ob meine Variablen derselben Sache zugewiesen sind oder ob sie nur gleich aussehen.

Jeder Wert ist eine ID, bei der es sich um eine eindeutige Nummer handelt, die sich darauf bezieht, wo er im Speicher des Computers gespeichert ist.

lontgomjr
quelle
5

Wenn Sie Python 3.4.1 verwenden, erhalten Sie eine andere Antwort auf Ihre Frage.

list = [1,2,3]
id(list[0])
id(list[1])
id(list[2])

kehrt zurück:

1705950792   
1705950808  # increased by 16   
1705950824  # increased by 16

Die Ganzzahlen -5, 256die eine konstante ID haben sollen, und wenn sie mehrmals gefunden werden, ändert sich ihre ID nicht, im Gegensatz zu allen anderen Zahlen davor oder danach, die jedes Mal unterschiedliche IDs haben, wenn Sie sie finden. Die Zahlen von -5bis 256haben IDs in aufsteigender Reihenfolge und unterscheiden sich um 16.

Die von der id()Funktion zurückgegebene Nummer ist eine eindeutige ID für jedes im Speicher gespeicherte Element und entspricht analog dem Speicherort in C.

Dhvanan Shah
quelle
2

Die Antwort ist so gut wie nie. IDs werden hauptsächlich intern in Python verwendet.

Der durchschnittliche Python-Programmierer wird wahrscheinlich nie id()in seinem Code verwenden müssen.

Gareth Latty
quelle
3
Vielleicht bin ich nicht durchschnittlich, aber ich benutze id()ziemlich viel. Zwei Anwendungsfälle auf den ersten Blick: Ein (handgerolltes, ad-hoc) Identitätsdiktat und ein Brauch repr()für ein Objekt, bei dem Identität wichtig ist, die Standardeinstellung reprjedoch nicht angemessen ist.
5
@ Delnan Ich würde nicht behaupten, dass dies häufige Fälle sind.
Gareth Latty
2

Der isOperator prüft damit, ob zwei Objekte identisch sind (im Gegensatz zu gleich). Der tatsächliche Wert, von dem zurückgegeben wird, id()wird so gut wie nie für irgendetwas verwendet, da er keine wirkliche Bedeutung hat und plattformabhängig ist.

omz
quelle
2

Dies ist die Adresse des Objekts im Speicher, genau wie im Dokument angegeben. Es sind jedoch Metadaten angehängt. Zum Speichern der Metadaten werden Eigenschaften des Objekts und der Speicherort im Speicher benötigt. Wenn Sie also Ihre Variable namens list erstellen, erstellen Sie auch Metadaten für die Liste und ihre Elemente.

Wenn Sie also kein absoluter Guru in der Sprache sind, können Sie die ID des nächsten Elements Ihrer Liste nicht anhand des vorherigen Elements bestimmen, da Sie nicht wissen, was die Sprache zusammen mit den Elementen zuweist.

Lajos Arpad
quelle
1
Ein Python-Guru zu sein, wird eigentlich nicht viel nützen, wenn man ein Objekt vorhersagt id(). Sie müssen mit den betreffenden Speichermanagern (Plural!) Gut vertraut sein, ihren genauen Status zu einem bestimmten Zeitpunkt kennen und die genaue Reihenfolge kennen, in der die Objekte zugewiesen wurden. Mit anderen Worten: Wird nicht passieren.
Sie sind ein absoluter Python-Guru, wenn Sie alle Nuancen einschließlich der möglichen Speichermanager genau kennen.
Lajos Arpad
1
Python zu kennen und eine bestimmte Implementierung (z. B. CPython) zu kennen, sind zwei völlig verschiedene Dinge. Und selbst CPython von innen nach außen zu kennen, hilft nicht weiter, da es mehrere Speichermanager gibt, die CPython aufruft und die nicht Teil von CPython, sondern Teil des jeweiligen Betriebssystems sind. Und wie ich bereits sagte, hilft es Ihnen nicht, all diese Nicht-Python-Dinge zu kennen, da die tatsächlichen Adressen vom Laufzeitstatus der Speichermanager abhängen und manchmal sogar zufällig sind.
1
Ganz zu schweigen davon, dass es nutzlos wäre zu wissen, da sich das Verlassen auf so etwas auf ein Implementierungsdetail stützen würde und daher fragil und unflexibel wäre.
Gareth Latty
2

Ich habe eine Idee, den Wert von id()bei der Protokollierung zu verwenden.
Es ist billig zu bekommen und es ist ziemlich kurz.
In meinem Fall verwende ich Tornado und id()möchte einen Anker haben, um Nachrichten zu gruppieren, die über eine Datei nach Web-Socket verteilt und gemischt sind.

Daneel S. Yaitskov
quelle
0

Ab Python 3 wird die ID einem Wert zugewiesen, der keine Variable ist. Dies bedeutet, dass, wenn Sie zwei Funktionen wie unten erstellen, alle drei IDs gleich sind.

>>> def xyz():
...     q=123
...     print(id(q))
...
>>> def iop():
...     w=123
...     print(id(w))
>>> xyz()
1650376736
>>> iop()
1650376736
>>> id(123)
1650376736
Asish Kumar
quelle
Obwohl dies früheren Erklärungen widerspricht, scheint es zu passieren und ich weiß nicht warum. Irgendeine Erklärung? Zum Beispiel x [0] = "abc" x [1] = "def" q = ["abc", "def"]. Id (x) == Id (q) ist wahr. Zufall?
VMMF
0

Ich bin etwas spät dran und werde über Python3 sprechen. Betrachten Sie das folgende Beispiel, um zu verstehen, was id () ist und wie es (und Python) funktioniert:

>>> x=1000
>>> y=1000
>>> id(x)==id(y)
False
>>> id(x)
4312240944
>>> id(y)
4312240912
>>> id(1000)
4312241104
>>> x=1000
>>> id(x)
4312241104
>>> y=1000
>>> id(y)
4312241200

Sie müssen alles auf der rechten Seite als Objekte betrachten. Jedes Mal, wenn Sie eine Zuweisung vornehmen, erstellen Sie ein neues Objekt und das bedeutet eine neue ID. In der Mitte sehen Sie ein "wildes" Objekt, das nur für die Funktions-ID (1000) erstellt wird. Die Lebensdauer gilt also nur für diese Codezeile. Wenn Sie die nächste Zeile überprüfen, sehen Sie, dass beim Erstellen der neuen Variablen x dieselbe ID wie bei diesem Platzhalterobjekt vorhanden ist. Es funktioniert so ziemlich wie eine Speicheradresse.

Tomas
quelle
0

Seien Sie vorsichtig (bezüglich der Antwort unten) ... Das stimmt nur, weil 123 zwischen -5 und 256 liegt ...

In [111]: q = 257                                                         

In [112]: id(q)                                                            
Out[112]: 140020248465168

In [113]: w = 257                                                         

In [114]: id(w)                                                           
Out[114]: 140020274622544

In [115]: id(257)                                                         
Out[115]: 140020274622768
gee
quelle