web-dev-qa-db-fra.com

Accéder à l'adresse mémoire de l'objet

Lorsque vous appelez la méthode object.__repr__() dans Python, vous obtenez quelque chose comme ceci:

<__main__.Test object at 0x2aba1c0cf890> 

Existe-t-il un moyen de récupérer l'adresse de la mémoire si vous surchargez __repr__(), autrement qu'en appelant super(Class, obj).__repr__() et en l'exécutant?

148
thr

Le manuel Python dit ceci à propos de id():

Renvoie l '"identité" d'un objet. Il s'agit d'un entier (ou entier long) qui est garanti d'être unique et constant pour cet objet tout au long de sa vie. Deux objets dont la durée de vie ne se chevauchent pas peuvent avoir la même valeur id (). (Note d'implémentation: il s'agit de l'adresse de l'objet.)

Donc, dans CPython, ce sera l'adresse de l'objet. Aucune garantie de ce type pour tout autre Python, cependant.

Notez que si vous écrivez une extension C, vous avez un accès complet aux éléments internes de l’interprète Python, y compris l’accès direct aux adresses des objets.

183
Nick Johnson

Vous pouvez réimplémenter le repr par défaut de cette façon:

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

Juste utiliser

id(object)
46
Ben Hoffstein

Il y a quelques problèmes ici qui ne sont couverts par aucune des autres réponses.

Tout d'abord, id renvoie uniquement:

"l'identité" d'un objet. C'est un entier (ou un entier long) qui est garanti d'être unique et constant pour cet objet pendant sa durée de vie. Deux objets dont la durée de vie ne se chevauchent pas peuvent avoir la même valeur id().


Dans CPython, il s’agit du pointeur sur le PyObject qui représente l’objet dans l’interpréteur, ce qui est la même chose que object.__repr__. Mais ceci est juste un détail d’implémentation de CPython, pas quelque chose qui est vrai de Python en général. Jython ne traite pas les pointeurs, il traite dans Java références (ce que la machine virtuelle Java représente bien sûr probablement comme des pointeurs, mais vous ne pouvez pas les voir et vous ne voudriez pas, car le GC est autorisé à les déplacer.) PyPy permet à différents types d’avoir différents types de id, mais le plus général est juste un index dans une table d'objets que vous avez appelée id, ce qui ne sera évidemment pas un pointeur. Je ne suis pas sûr pour IronPython, mais je suspecterais à cet égard, il ressemble plus à Jython qu'à CPython. Donc, dans la plupart des implémentations Python, il n'y a aucun moyen d'obtenir ce qui est apparu dans ce repr, et cela ne sert à rien si vous le faisiez. .


Mais que se passe-t-il si vous ne vous souciez que de CPython? C'est un cas assez commun, après tout.

Tout d’abord, vous remarquerez peut-être que id est un entier; * si vous voulez que la chaîne 0x2aba1c0cf890 au lieu du nombre 46978822895760, vous devrez la formater vous-même. Sous les couvertures, je crois que object.__repr__ utilise finalement le format %p de printf, que vous n'avez pas dans Python… mais vous pouvez toujours le faire:

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

* Dans 3.x, c'est un int. Dans 2.x, c'est un int s'il est assez gros pour contenir un pointeur (ce qui n'est peut-être pas dû à des problèmes de numéros signés sur certaines plates-formes) et un long sinon.

Y a-t-il quelque chose que vous puissiez faire avec ces pointeurs en plus de les imprimer? Bien sûr (encore une fois, en supposant que vous ne vous souciez que de CPython).

Toutes les fonctions C API prennent un pointeur sur un PyObject ou un type associé. Pour ces types liés, vous pouvez simplement appeler PyFoo_Check pour vous assurer qu'il s'agit bien d'un objet Foo, puis transtyper avec (PyFoo *)p. Donc, si vous écrivez une extension C, le id est exactement ce dont vous avez besoin.

Que faire si vous écrivez un code pur Python?)? Vous pouvez appeler exactement les mêmes fonctions avec pythonapi à partir de ctypes.


Enfin, quelques-unes des autres réponses ont évoqué ctypes.addressof . Ce n'est pas pertinent ici. Cela ne fonctionne que pour les objets ctypes tels que c_int32 (et peut-être quelques objets de type mémoire tampon, comme ceux fournis par numpy). Et, même là, il ne vous donne pas l'adresse de la valeur c_int32, il vous donne l'adresse du niveau C int32 que le c_int32 termine.

Cela étant dit, le plus souvent, si vous pensez vraiment avoir besoin de l'adresse de quelque chose, vous ne voulez pas d'objet natif Python en premier lieu, vous voulez un ctypes objet.

21
abarnert

Juste en réponse à Torsten, je n’ai pas pu appeler addressof() avec un objet python régulier. En outre, id(a) != addressof(a). Ceci est en CPython, Je ne sais rien d'autre.

>>> 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
13
Peter Le Bek

Avec ctypes , vous pouvez obtenir la même chose avec

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

Documentation:

addressof(C instance) -> integer
Renvoie l'adresse du tampon interne de l'instance C

Notez que dans CPython, actuellement id(a) == ctypes.addressof(a), mais ctypes.addressof Devrait renvoyer la véritable adresse pour chaque Python, si

  • ctypes est supporté
  • les pointeurs de mémoire sont une notion valide.

Edit : ajout d'informations sur l'indépendance de l'interpréteur des ctypes

4
Torsten Marek

Vous pouvez obtenir quelque chose d’approprié à cet effet avec:

id(self)
2
Thomas Wouters

S'il est vrai que id(object) obtient l'adresse de l'objet dans l'implémentation CPython par défaut, c'est généralement inutile ... vous ne pouvez pas faire tout ce qui a l’adresse de pure Python.

Le seul moment où vous seriez réellement en mesure d'utiliser l'adresse provient d'une bibliothèque d'extension C ... auquel cas il est trivial d'obtenir l'adresse de l'objet depuis Python sont toujours transmis en tant que C pointeurs.

0
Dan Lenski