Acceso a la dirección de la memoria de objetos

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

  

¿Hay alguna manera de obtener una retención de la dirección de memoria si sobrecarga __repr__() , aparte de llamar a super(Class, obj).__repr__() y volver a expresslo?

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

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

Así que 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.

Podrías volver a implementar la reproducción por defecto de esta manera:

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

Solo usa

 id(object) 

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

Primero, id solo devuelve:

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


En CPython, este es el puntero al PyObject que representa el objeto en el intérprete, que es lo mismo que muestra el object.__repr__ . 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 los punteros, se trata de las referencias de Java (que JVM, por supuesto, 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 a los que has llamado id , que obviamente no será un puntero. No estoy seguro acerca de IronPython, pero sospecho que es más como Jython que como CPython en este sentido. Por lo tanto, en la mayoría de las implementaciones de Python, no hay forma de que aparezca algo en esa repr , y no sirve de nada si lo hiciste.


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

Bueno, primero, puedes notar que id es un número entero; * si quieres esa cadena 0x2aba1c0cf890 lugar del número 46978822895760 , tendrás que formatearlo tú mismo. Debajo de las cubiertas, creo que object.__repr__ está usando el formato %p printf , que no tiene Python … pero siempre puede 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 int si es lo suficientemente grande como para contener un puntero, que puede no ser debido a problemas de números firmados en algunas plataformas, y long .

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

Todas las funciones de la API de C llevan un puntero a un PyObject o un tipo relacionado. Para esos tipos relacionados, puedes simplemente llamar a PyFoo_Check para asegurarte de que realmente es un objeto Foo , luego lanzar con (PyFoo *)p . Entonces, si estás escribiendo una extensión C, el id es exactamente lo que necesitas.

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


Finalmente, algunas de las otras respuestas han ctypes.addressof . Eso no es relevante aquí. Esto solo funciona para objetos ctypes como c_int32 (y quizás algunos objetos similares a búfer de memoria, como los que proporciona numpy ). Y, incluso allí, no le está dando la dirección del valor c_int32 , le está dando la dirección del nivel C int32 que el c_int32 envuelve.

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

Solo en respuesta a Torsten, no pude llamar a addressof() en 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 "", line 1, in  TypeError: invalid type >>> b = c_int(69) >>> addressof(b) 4300673472 >>> id(b) 4300673392 

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 de C

Tenga en cuenta que en CPython, actualmente id(a) == ctypes.addressof(a) , pero ctypes.addressof deberí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.

Edición : información añadida sobre intérprete-independencia de ctypes

Puede obtener algo adecuado para ese propósito con:

 id(self) 

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

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