Zugriff auf die Objektspeicheradresse


168

Wenn Sie die object.__repr__()Methode in Python aufrufen, erhalten Sie Folgendes zurück:

<__main__.Test object at 0x2aba1c0cf890> 

Gibt es eine Möglichkeit, die Speicheradresse zu erhalten, wenn Sie überladen sind __repr__(), super(Class, obj).__repr__()außer sie aufzurufen und neu zu drucken?

Antworten:


208

Das Python-Handbuch enthält Folgendes id():

Geben Sie 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. (Implementierungshinweis: Dies ist die Adresse des Objekts.)

In CPython ist dies also die Adresse des Objekts. Keine solche Garantie für andere Python-Interpreter.

Beachten Sie, dass Sie beim Schreiben einer C-Erweiterung vollen Zugriff auf die Interna des Python-Interpreters haben, einschließlich des direkten Zugriffs auf die Adressen von Objekten.


7
Dies ist keine universelle Antwort auf die Frage; Dies gilt nur für CPython.
DilithiumMatrix

5
Hinweis für sich selbst: Die Garantie gilt nicht für Multiprocessing
Rufus

1
Einige Möglichkeiten, es zu verwenden (um den darin enthaltenen Wert zu vergleichen): forum.freecodecamp.com/t/python-id-object/19207
J. Does

Worauf bezieht sich ein Objekt lifetime(und was bedeutet es für die Lebensdauer overlap/not overlap) in diesem Zusammenhang?
Minh Tran

4
@MinhTran Da die ID die Speicheradresse des Objekts ist, ist sie innerhalb des Prozesses garantiert eindeutig, und solange das Objekt vorhanden ist. Einige Zeit, nachdem das Objekt durch Müll gesammelt wurde, kann der Speicher wiederverwendet werden. Eine nicht überlappende Lebensdauer würde bedeuten, dass das ursprüngliche Objekt nicht mehr vorhanden ist, wenn das neue Objekt erstellt wird. Diese Einschränkung bedeutet also, dass Sie id () nicht sicher verwenden können, um einen Hash eines Objekts zu erstellen, um es zu speichern, freizugeben und später wieder herzustellen.
Joshua Clayton

71

Sie können den Standard-Repr folgendermaßen neu implementieren:

def __repr__(self):
    return '<%s.%s object at %s>' % (
        self.__class__.__module__,
        self.__class__.__name__,
        hex(id(self))
    )

1
Ich weiß, dass dies alt ist, aber Sie können es einfach tun return object.__repr__(self)oder sogar tun, object.__repr__(obj)wann immer Sie dies brauchen, anstatt eine neue Klasse zu bilden
Artyer

2
@Artyer: Was hat dieser Kommentar mit der ursprünglichen Frage zu tun? Die hier veröffentlichte Antwort erstellt die Adresse neu, wie von der ursprünglichen Frage angefordert. Müssten Sie nicht die Schnur zerfleischen, wenn Sie es so machen würden, wie Sie es vorschlagen?
Rafe

1
Dies scheint mir die beste Antwort zu sein. Versuchen Sie einfach, ein Objekt () zu erstellen, drucken Sie es aus und drucken Sie dann hex (id (Objekt)). Die Ergebnisse stimmen überein
Rafe

@Rafe Ihre Antwort ist langwierig __repr__ = object.__repr__und bei weitem nicht so narrensicher , da es eine Vielzahl von Situationen gibt, in denen dies nicht funktioniert, z. B. eine überschriebene __getattribute__oder eine Nicht-CPython-Implementierung, in der die ID nicht vorhanden ist der Speicherort. Es wird auch nicht mit Z gefüllt, sodass Sie herausfinden müssen, ob das System 64-Bit ist, und die Nullen nach Bedarf hinzufügen müssen.
Artyer

@Artyer: Mein Beispiel zeigt, wie man einen Repr erstellt. Wir fügen häufig benutzerdefinierte Informationen hinzu (und ich würde sagen, dies ist eine gute Codierungspraxis, da sie beim Debuggen hilft). Wir verwenden diesen Stil stark und ich bin noch nie auf Ihre Randfälle gestoßen. Danke, dass du sie geteilt hast!
Rafe


24

Hier gibt es einige Probleme, die in keiner der anderen Antworten behandelt werden.

Erstens, gibt idnur zurück:

die "Identität" eines Objekts. 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.


In CPython ist dies zufällig der Zeiger auf das PyObjectObjekt, das das Objekt im Interpreter darstellt. Dies wird auch object.__repr__angezeigt. Dies ist jedoch nur ein Implementierungsdetail von CPython, nicht etwas, das für Python im Allgemeinen gilt. Jython handelt nicht mit Zeigern, sondern mit Java-Referenzen (die die JVM wahrscheinlich als Zeiger darstellt, aber Sie können diese nicht sehen - und möchten dies auch nicht, da der GC sie verschieben darf). Mit PyPy können verschiedene Typen unterschiedliche Arten haben id, aber am allgemeinsten ist nur ein Index in einer Tabelle von Objekten, die Sie aufgerufen habenidon, was offensichtlich kein Zeiger sein wird. Ich bin mir bei IronPython nicht sicher, aber ich würde vermuten, dass es in dieser Hinsicht eher wie Jython als wie CPython ist. In den meisten Python-Implementierungen gibt es also keine Möglichkeit, das zu erhalten, was darin angezeigt wird repr, und es nützt nichts, wenn Sie dies getan haben.


Aber was ist, wenn Sie sich nur für CPython interessieren? Das ist schließlich ein ziemlich häufiger Fall.

Zunächst stellen Sie möglicherweise fest, dass ides sich um eine Ganzzahl handelt. * Wenn Sie diese 0x2aba1c0cf890Zeichenfolge anstelle der Zahl verwenden möchten 46978822895760, müssen Sie sie selbst formatieren. Unter dem Deckmantel glaube ich, dass object.__repr__letztendlich printfdas %pFormat verwendet wird, das Sie nicht von Python haben ... aber Sie können dies immer tun:

format(id(spam), '#010x' if sys.maxsize.bit_length() <= 32 else '#18x')

* In 3.x ist es ein int. In 2.x ist dies ein intFall, der groß genug ist, um einen Zeiger aufzunehmen - was möglicherweise nicht auf Probleme mit signierten Nummern auf einigen Plattformen zurückzuführen ist - und ein longanderes.

Können Sie mit diesen Zeigern etwas anderes tun, als sie auszudrucken? Sicher (wieder vorausgesetzt, Sie interessieren sich nur für CPython).

Alle C-API- Funktionen nehmen einen Zeiger auf einen PyObjectoder einen verwandten Typ. Für diese verwandten Typen können Sie einfach aufrufen PyFoo_Check, um sicherzustellen, dass es sich wirklich um ein FooObjekt handelt, und dann mit umwandeln (PyFoo *)p. Wenn Sie also eine C-Erweiterung schreiben, idist dies genau das, was Sie brauchen.

Was ist, wenn Sie reinen Python-Code schreiben? Sie können genau die gleichen Funktionen mit pythonapifrom aufrufen ctypes.


Schließlich haben sich einige der anderen Antworten ergeben ctypes.addressof. Das ist hier nicht relevant. Dies funktioniert nur für ctypesObjekte wie c_int32(und möglicherweise einige speicherpufferähnliche Objekte wie die von numpy). Und selbst dort erhalten Sie nicht die Adresse des c_int32Werts, sondern die Adresse der C-Ebene int32, die c_int32der Abschluss enthält.

Davon abgesehen wollten Sie meistens, wenn Sie wirklich glauben, die Adresse von etwas zu benötigen, überhaupt kein natives Python-Objekt, sondern ein ctypesObjekt.


Nun,

@Enerccio Die anderen Verwendungszwecke von id- einschließlich der Verwendung, um veränderbare Werte in einer seenMenge oder einem Diktat zu speichern - cachehängen in keiner Weise davon ab, ob ides sich um einen Zeiger handelt oder in irgendeiner Weise mit dem repr. Genau aus diesem Grund funktioniert dieser Code in allen Python-Implementierungen, anstatt nur in CPython.
Abarnert

Ja, ich habe ides verwendet, aber ich meine, auch in Java kann man die Adresse des Objekts erhalten. Es scheint seltsam, dass es in (C) Python keinen Weg gibt, da dieses tatsächlich ein stabiles GC hat, das keine Objekte bewegt. Die Adresse bleibt also gleich
Enerccio

@Enerccio Sie möchten jedoch nicht die Adresse eines Objekts für einen zwischenspeicherbaren Wert verwenden - Sie möchten die Adresse ideines Objekts verwenden, unabhängig davon, ob es sich um eine Adresse handelt oder nicht. In PyPy idist dies beispielsweise immer noch genauso nützlich wie ein Schlüssel in CPython, obwohl es normalerweise nur ein Index für eine versteckte Tabelle in der Implementierung ist. Ein Zeiger wäre jedoch nutzlos, da das Objekt (wie Java) verschoben werden kann Erinnerung.
Abarnert

@Enerccio Wie auch immer, es gibt eine Möglichkeit, einen Zeiger in CPython zu erhalten. Wie in der Antwort erläutert, dokumentiert CPython explizit als implementierungsspezifisches Detail, dass das ideines Objekts der Zeiger auf die Position des Objekts im Speicher ist. Wenn Sie also den Zeigerwert (den Sie fast nie verwenden, wie auch in der Antwort erläutert) in CPython-spezifischem Code verwenden, gibt es eine Möglichkeit, ihn zu erhalten, der dokumentiert ist und garantiert funktioniert.
Abarnert

13

Nur als Antwort auf Torsten konnte ich kein addressof()normales Python-Objekt aufrufen . Darüber hinaus id(a) != addressof(a). Dies ist in CPython, weiß nichts anderes.

>>> from ctypes import c_int, addressof
>>> a = 69
>>> addressof(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: invalid type
>>> b = c_int(69)
>>> addressof(b)
4300673472
>>> id(b)
4300673392

4

Mit ctypes können Sie dasselbe erreichen

>>> import ctypes
>>> a = (1,2,3)
>>> ctypes.addressof(a)
3077760748L

Dokumentation:

addressof(C instance) -> integer
Gibt die Adresse des internen Puffers der C-Instanz zurück

Beachten Sie, dass in CPython derzeit id(a) == ctypes.addressof(a)jedoch ctypes.addressofdie tatsächliche Adresse für jede Python-Implementierung zurückgegeben werden sollte, wenn

  • ctypes wird unterstützt
  • Speicherzeiger sind ein gültiger Begriff.

Bearbeiten : Informationen zur Interpreterunabhängigkeit von ctypes hinzugefügt


13
>>> ctypes importieren >>> a = (1,2,3) >>> ctypes.addressof (a) Traceback (letzter Aufruf zuletzt): Datei "<input>", Zeile 1, in <module> TypeError: ungültiger Typ >>> id (a) 4493268872 >>>

5
Ich stimme Barry zu: Der obige Code ergibt sich, TypeError: invalid typewenn ich ihn mit Python 3.4 versuche.
Brandon Rhodes


1

Ich weiß, dass dies eine alte Frage ist, aber wenn Sie noch programmieren, in Python 3 in diesen Tagen ... Ich habe tatsächlich festgestellt, dass es eine wirklich einfache Möglichkeit gibt, dies zu tun, wenn es sich um eine Zeichenfolge handelt:

>>> spam.upper
<built-in method upper of str object at 0x1042e4830>
>>> spam.upper()
'YO I NEED HELP!'
>>> id(spam)
4365109296

Die Zeichenfolgenkonvertierung wirkt sich auch nicht auf den Speicherort im Speicher aus:

>>> spam = {437 : 'passphrase'}
>>> object.__repr__(spam)
'<dict object at 0x1043313f0>'
>>> str(spam)
"{437: 'passphrase'}"
>>> object.__repr__(spam)
'<dict object at 0x1043313f0>'

0

Es stimmt zwar, dass id(object)die Adresse des Objekts in der Standard-CPython-Implementierung abgerufen wird, dies ist jedoch im Allgemeinen nutzlos. Mit der Adresse aus reinem Python-Code können Sie nichts anfangen.

Das einzige Mal, dass Sie die Adresse tatsächlich verwenden können, stammt aus einer C-Erweiterungsbibliothek. In diesem Fall ist es trivial, die Adresse des Objekts abzurufen, da Python-Objekte immer als C-Zeiger herumgereicht werden.


1
Es sei denn, Sie verwenden das integrierte ctypesToolkit in der Standardbibliothek. In diesem Fall können Sie alle möglichen Dinge mit der Adresse tun :)
Brandon Rhodes
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.