Acceso a la dirección de memoria del objeto

168

Cuando llamas al object.__repr__()método en Python, obtienes algo como esto:

<__main__.Test object at 0x2aba1c0cf890> 

¿Hay alguna forma de obtener la dirección de memoria si se sobrecarga __repr__(), aparte de llamar super(Class, obj).__repr__()y regexearla?

thr
fuente

Respuestas:

208

El manual de Python tiene esto que decir sobre id():

Devuelve la "identidad" de un objeto. Este es un entero (o entero largo) que se garantiza que es único y constante para este objeto durante su vida útil. Dos objetos con vidas que no se superponen pueden tener el mismo valor id (). (Nota de implementación: esta es la dirección del objeto).

Entonces, en CPython, esta será la dirección del objeto. Sin embargo, no existe tal garantía para ningún otro intérprete de Python.

Tenga en cuenta que si está escribiendo una extensión C, tiene acceso completo a las partes internas del intérprete de Python, incluido el acceso a las direcciones de los objetos directamente.

Nick Johnson
fuente
77
Esta no es una respuesta universal a la pregunta; solo se aplica a CPython.
DilithiumMatrix
55
Nota personal: la garantía no se aplica al multiprocesamiento
Rufus
1
Algunas formas de usarlo (para comparar el valor que contiene): forum.freecodecamp.com/t/python-id-object/19207
J. ¿El
¿A qué se refiere un objeto lifetime(y qué significa para toda la vida overlap/not overlap) en este contexto?
Minh Tran
44
@MinhTran porque la identificación es la dirección de memoria del objeto, se garantiza que es única dentro del proceso y mientras exista el objeto. Algún tiempo después de que el objeto se haya recolectado, la memoria puede reutilizarse. Una vida útil no superpuesta significaría que el objeto original ya no existe cuando se crea el nuevo objeto. Entonces, esta limitación significa que no puede usar id () de manera segura para crear un hash de un objeto para almacenar, liberarlo y luego restablecerlo.
Joshua Clayton el
71

Puede volver a implementar la reproducción predeterminada de esta manera:

def __repr__(self):
    return '<%s.%s object at %s>' % (
        self.__class__.__module__,
        self.__class__.__name__,
        hex(id(self))
    )
Armin Ronacher
fuente
1
Sé que esto es viejo, pero puedes hacer return object.__repr__(self)o incluso hacer object.__repr__(obj)cuando lo necesites en lugar de hacer una nueva clase
Artyer el
2
@Artyer: ¿Qué tiene que ver este comentario con la pregunta original? La respuesta publicada aquí es recrear la dirección solicitada por la pregunta original. ¿No tendrías que forzar la cadena si lo hicieras de la manera que sugieres?
Rafe
1
Esta parece ser la mejor respuesta para mí. Simplemente intente hacer un objeto (), imprímalo, luego imprima hexadecimal (id (objeto)) y los resultados coinciden
Rafe
@Rafe Su respuesta es una forma larga y sin aliento __repr__ = object.__repr__, y no es tan infalible, ya que hay una variedad de situaciones en las que esto no funciona, por ejemplo, una __getattribute__implementación anulada o no CPython donde la identificación no es La ubicación de la memoria. Tampoco se llena en z, por lo que tendría que averiguar si el sistema es de 64 bits y agregar los ceros según sea necesario.
Artyer
@Artyer: mi ejemplo muestra cómo construir una repr. A menudo agregamos información personalizada (y diría que esta es una buena práctica de codificación, ya que ayuda a la depuración). Utilizamos mucho este estilo y nunca me he encontrado con sus casos extremos. Gracias por compartirlos!
Rafe
52

Solo usa

id(object)
Ben Hoffstein
fuente
66
que da un número ... ¿Que sigue? ¿Puedo acceder al objeto con ese número?
JLT
Puedes consultar esto id()@JLT
Billal Begueradj
24

Aquí hay algunos problemas que no están cubiertos por ninguna de las otras respuestas.

Primero, idsolo devuelve:

La "identidad" de un objeto. Este es un entero (o entero largo) que se garantiza que es único y constante para este objeto durante su vida útil. Dos objetos con vidas que no se superponen pueden tener el mismo id()valor.


En CPython, resulta ser el puntero al PyObjectque representa el objeto en el intérprete, que es lo mismo que en, que obviamente no será un puntero. No estoy seguro acerca de IronPython, pero sospecharía que se parece más a Jython que a CPython a este respecto. Por lo tanto, en la mayoría de las implementaciones de Python, no hay forma de obtener lo que se muestra en eso , y no sirve de nada si lo hiciera.object.__repr__ muestra. Pero esto es solo un detalle de implementación de CPython, no algo que sea cierto de Python en general. Jython no se ocupa de punteros, se trata de referencias de Java (que la JVM, por supuesto, probablemente representa como punteros, pero no puede verlos, y no querría hacerlo, porque el GC puede moverlos). PyPy permite que diferentes tipos tengan diferentes tipos de id, pero el más general es solo un índice en una tabla de objetos que ha llamadoidrepr


Pero, ¿y si solo te importa CPython? Ese es un caso bastante común, después de todo.

Bueno, primero, puede notar que ides un número entero; * si desea esa 0x2aba1c0cf890cadena en lugar del número 46978822895760, tendrá que formatearla usted mismo. Debajo de las cubiertas, creo object.__repr__que finalmente está usando printfel %pformato de, que no tienes de Python ... pero siempre puedes hacer esto:

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

* En 3.x, es un int. En 2.x, es un intsi es lo suficientemente grande como para contener un puntero, lo que puede no ser debido a problemas de números firmados en algunas plataformas, y de lo longcontrario.

¿Hay algo que pueda hacer con estos punteros además de imprimirlos? Claro (de nuevo, suponiendo que solo te preocupes por CPython).

Todas las funciones de la API de C llevan un puntero a uno PyObjecto un tipo relacionado. Para esos tipos relacionados, solo puede llamar PyFoo_Checkpara asegurarse de que realmente es un Fooobjeto y luego emitir con (PyFoo *)p. Entonces, si está escribiendo una extensión C, esto ides exactamente lo que necesita.

¿Qué pasa si estás escribiendo código Python puro? Puede invocar exactamente las mismas funciones con pythonapifrom ctypes.


Finalmente, algunas de las otras respuestas han aparecido ctypes.addressof. Eso no es relevante aquí. Esto solo funciona para ctypesobjetos como c_int32(y tal vez algunos objetos similares al búfer de memoria, como los proporcionados pornumpy ). E incluso allí, no le está dando la dirección del c_int32valor, le está dando la dirección del nivel C int32que c_int32concluye.

Dicho esto, la mayoría de las veces, si realmente crees que necesitas la dirección de algo, no querías un objeto nativo de Python en primer lugar, querías un ctypesobjeto.

abarnert
fuente
bueno, esta es la única forma de almacenar objetos mutables en mapas / conjuntos cuando la identidad es importante ...
Enerccio
@Enerccio Los otros usos de, idincluido el uso de ellos para mantener valores mutables en un seenconjunto o un cachedict, no dependen de ninguna manera de idser un puntero, o relacionados de ninguna manera con el repr. Es por eso que dicho código funciona en todas las implementaciones de Python, en lugar de solo funcionar en CPython.
abarnert
sí, solía idhacerlo, pero quiero decir que incluso en Java puedes obtener la dirección del objeto, parece extraño que no haya forma en (C) Python ya que ese tiene un gc realmente estable que no moverá objetos, por lo que la dirección permanece igual
Enerccio
@Enerccio Pero no desea utilizar la dirección de un objeto para un valor almacenable en caché, desea utilizar el idobjeto, ya sea una dirección o no. Por ejemplo, en PyPy, idsigue siendo tan útil como una clave en CPython, aunque generalmente es solo un índice en alguna tabla oculta en la implementación, pero un puntero sería inútil, porque (como Java) el objeto puede moverse memoria.
abarnert
@Enerccio De todos modos, no es una forma de obtener un puntero en CPython. Como se explica en la respuesta, CPython documenta explícitamente, como un detalle específico de la implementación, que el idde un objeto es el puntero a la ubicación del objeto en la memoria. Entonces, si tiene algún uso para el valor del puntero (que casi nunca hace, como también se explica en la respuesta) en el código específico de CPython, hay una manera de obtenerlo que esté documentado y garantizado que funcione.
abarnert
13

Solo en respuesta a Torsten, no pude llamar addressof()a un objeto python normal. Además, id(a) != addressof(a). Esto está en CPython, no sé nada más.

>>> 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
Peter Le Bek
fuente
4

Con ctypes , puedes lograr lo mismo con

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

Documentación:

addressof(C instance) -> integer
Devuelve la dirección del búfer interno de la instancia C

Tenga en cuenta que en CPython, actualmente id(a) == ctypes.addressof(a), pero ctypes.addressofdebería devolver la dirección real para cada implementación de Python, si

  • ctypes es compatible
  • Los punteros de memoria son una noción válida.

Editar : se agregó información sobre la independencia del intérprete de los tipos

Torsten Marek
fuente
13
>>> import ctypes >>> a = (1,2,3) >>> ctypes.addressof (a) Traceback (última llamada más reciente): Archivo "<input>", línea 1, en <module> TypeError: tipo no válido >>> id (a) 4493268872 >>>
55
Estoy de acuerdo con Barry: el código anterior aparece TypeError: invalid typecuando lo intento con Python 3.4.
Brandon Rhodes
2

Puede obtener algo adecuado para ese propósito con:

id(self)
Thomas Wouters
fuente
1

Sé que esta es una vieja pregunta, pero si todavía estás programando, en Python 3 en estos días ... De hecho, he descubierto que si es una cadena, entonces hay una manera realmente fácil de hacer esto:

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

la conversión de cadena tampoco afecta la ubicación en la memoria:

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

Si bien es cierto que id(object)obtiene la dirección del objeto en la implementación predeterminada de CPython, esto generalmente es inútil ... no se puede hacer nada con la dirección del código Python puro.

La única vez que realmente podría usar la dirección es desde una biblioteca de extensiones C ... en cuyo caso es trivial obtener la dirección del objeto ya que los objetos Python siempre se pasan como punteros C.

Dan Lenski
fuente
1
A menos que use el ctypeskit de herramientas incorporado en la Biblioteca estándar. En cuyo caso puede hacer todo tipo de cosas con la dirección :)
Brandon Rhodes