Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Expérience sur les instances en mémoire , adresses et contenus

+7 votes

Voici une "expérience" sur les instances qui sont dans la mémoire :
Je crée une classe toute simple:

>>> class Personne():
    def __init__(self,nom="anonyme"):
        self.nom = nom

Je créer une première instance que j' appelle billy :

>>> billy=Personne()
>>> billy
<__main__.Personne object at 0x034DC070>

Maintenant voici l' expérience : je crée 3 instances sans recourir à une variable :

 >>> Personne()   
 <__main__.Personne object at 0x03B4DDF0>
 >>> Personne()
 <__main__.Personne object at 0x03B4DE10>
 >>> Personne()
 <__main__.Personne object at 0x03B4DE50>

Revenons sur ma première instance "billy" :

>>> billy.nom
'anonyme'

Je donne un attribut en rapport avec la variable qui m'a aidé à l'instancier :

>>> billy.nom = 'zekid'
>>> billy
<__main__.Personne object at 0x034DC070>
>>> billy.nom
'zekid'

Ma question est : comment je fais pour rattraper mes instances aux adresses 0x03B4DDF0, 0x03B4DE10, 0x03B4DE50 ?
Je sais que ma question peut paraître bizarre, mais je suis en train de lire un cours sur les pointeurs en C afin d' améliorer ma compréhension, et je suis curieux de savoir...

Pour prolonger l' experience, supposons que j'ai une classe Chronometre , et que je veuille créer des instances associées à un timestamp généré après un événement (un click).
on aurait ainsi une instance 1432923813.
Cette instance pourrait avoir un attribut tel que 1432923813.nom= "click n°5".
Comment créer cette instance si on ne connait pas à l' avance le timestamp ? Passer par une variable intermédiaire , ou existe il un moyen plus direct ?

J' ai fait de mon mieux pour énoncer le truc...

demandé 29-Mai-2015 par buffalo974 (2,886 points)

Effectivement très intéressant comme question donc je suis tout aussi impatient que toi d'obtenir une réponse.

4 Réponses

+5 votes
 
Meilleure réponse

Je mets de côté l'histoire du chronomètre pour l'instant.

Ma question est : comment je fais pour rattraper mes instances aux adresses 0x03B4DDF0, 0x03B4DE10, 0x03B4DE50 ?

Tu ne peux pas.

Et désolé, mais les pointeurs du C ne vont pas t'aider à mieux comprendre le fonctionnement de Python sur ces aspects. Entendons nous bien, tu as tout à gagner à comprendre les pointeurs, mais c'est juste qu'il n'y a pas à proprement parler de pointeurs nativement en Python.

Je recommande de consulter l'intervention de Ned Batchelder à la pycon2015 [1]. C'est très éclairant.

Donc, pourquoi on peut pas récupérer ces instances "anonymes" ? Il faut comprendre comment sont gérés les objets en mémoire en python et surtout par quel miracle on peut se passer d'avoir à nettoyer la mémoire (pas comme en C donc).
Le miracle, c'est le "ramasse-miette" (garbage collector) de l'interpréteur Python, il regarde régulièrement si des choses "traînent" et il les détruit. En gros, quelque chose "traîne" quand plus rien ne lui fait référence, autrement dit quand on est sûr que plus personne ne pourra s'en servir.

Et c'est exactement ce qui arrive à tes trois instances "anonymes", tu ne les as pas "affecté à une variable" après lors création (voir [1] pour comprendre les guillemets).

Alors comme souvent en Python, il y a surement des moyens de tricher, on sait par exemple qu'il peut se passer un peu de temps avant que les miettes ne soient ramassées. Mais j'ai moi aussi une question : comment tu récupères les id de tes objets en pratique ? est-ce que ce n'est pas juste plus simple de garder une référence sur tes objets ?

Pour expérimenter et surtout visualiser les références aux objets sur vos propres scripts, il y a online python tutor [2] qui est juste fantastique.
Enfin, je rajoute une mention à la lib standard weakref [3] qui apparaît souvent sur ce genre de considérations.

Références

répondu 29-Mai-2015 par bubulle (2,256 points)
sélectionné 1-Jun-2015 par buffalo974

Tres bonne la vidéo.

Et pour weakref, il me parait utile de préciser son interet principal est d'éviter les références cycliques permettant au garbage collector de faire son job proprement.

0 votes

Pour le chronomètre.

Tu es en train de proposer de créer des noms automatiques ? basés sur des événements imprévisibles ? t'es sûr de ton coups là ?

Voilà ce que je te propose : remplace '1432923813.nom = "click n°5" ' par 'click n°5 .timestamp = 1432923813' . Tout devient plus simple, on écrira juste

class Click:
    def __init__(self, timestamp):
        self.timestamp = timestamp
    # rajoute ici le reste

clicks = []
# quelqu'un clique ? on récupère le timestamp et on enregistre
clicks.append(Click(timestamp))
répondu 29-Mai-2015 par bubulle (2,256 points)

Oui il s' agit de nommer de façon automatique les instances, de façon à ce que je puisse
les attraper avant qu'elle se fasse détruire par le garbage collector ^^

les appeller par le timestamp est certes étranges, mais pratique pour lui donner un nom.
Un peu comme quand on utilise un calendrier pour nommer un bébé ...

alors la proposition de jc avec les dict devrait aller:

clicks[1432923813] = nouveau_click

c'est finalement très proche de ta 1ère intention mais sans les complications inextricables du passage par l'id.

Juste pour être clair, en faisant ça tu crées une référence sur l'objet dans le dict donc pas besoin d'un nom dédié en plus. Ton objet vivra au moins aussi longtemps que le dict.

+3 votes

Apparament, c'est possible d'accéder a une instance a partir de son adresse mémoire avec cpython (voire ce commentaire SO)

Si tu as une référence vers ton instance (une variable qui pointe vers elle), tu peux récupérer son adresse avec la fonction id()

>>> import ctypes
>>> a = "hello world"
>>> print(ctypes.cast(id(a), ctypes.py_object).value)
hello world

Par contre, admettons que tu aies son adresse, mais pas de référence dessus (l'exemple que tu donnes) : a un moment (tu ne peux pas trop savoir quand), le garbage collector va passer et libérer l'espace mémoire qu'elle occupe.

>>> Personne(nom = 'bob')
<__main__.Personne object at 0x7f8e29933f98>
>>> print(ctypes.cast(0x7f8e29933f98, ctypes.py_object).value.nom)
bob
>>> import gc; gc.collect() # on force le passage du garbage collector
0
>>> print(ctypes.cast(0x7f8e29933f98, ctypes.py_object).value)
((<NULL>,),)

Donc si tu veux etre sur de pouvoir tout le temps accéder a tes instances en mémoire, il faut absolument que tu aies une référence dessus.

Si tu souhaites créer des variables dont tu ne connais pas le nom, tu peux les stocker dans un dict et générer la key de la manière que tu veux (mais avec cette solution, on s'éloigne de l'accès a une instance par son adresse mémoire)

répondu 29-Mai-2015 par jc (2,704 points)
ctypes.cast(id(a), ctypes.py_object).value

Wow, ça c'est pas mal... je me disais bien que c'était possible mais j'aurais jamais pensé à ctypes.

Ceci étant dit, je pense qu'il surtout retenir qu'une astuce pareil est un signal fort pour se demander si on fait vraiment les choses dans le bon sens.
Je proposais un liste dans ma réponse, tu proposes un dict, le choix dépend du contexte mais c'est clairement plus sain d'aller vers ce genre de solutions.

Tout a fait d'accord, l'usage me parait etre principalement réservé pour du debug.
J'ai vu que nos réponses se recoupaient sur plusieurs points
du coup j'upvote :)

+1 vote

Pour appuyer la réponse de @bubulle, je citerais un passage de l'article intéressant sur S&M traitant des valeurs et références en Python :

En Python, il n’existe donc aucun moyen de supprimer un objet. On peut
juste supprimer toutes ses références, et attendre que Python
s’aperçoive que tout le monde s’en branle à présent de l’objet, et le
supprime.

Dans certains cas particuliers, on veut qu’une référence à un objet ne
compte pas comme une voix. On peut le faire en utilisant le module
weakref, mais cela ne marche qu’avec les classes que l’on code
soit-même

class ALaVanille(object):
    pass

>>> import weakref
>>> g1 = ALaVanille()
>>> g2 = weakref.proxy(g1)
>>> g2
<weakproxy at 0x7f5f26994730 to ALaVanille at 0x7f5f26992d90>
>>> g1.foo = "bar"
>>> g2.foo
'bar'
>>> del g1
>>> g2
<weakproxy at 0x7f5f26994730 to NoneType at 0x859380>

Dès qu’on supprime g1, g2 proxy vers None, car la référence vers
l’instance de ALaVanille ne compte pas pour garder en vie l’objet.

répondu 29-Mai-2015 par boblinux (3,092 points)

Auriez vous un exemple supplémentaire pour illustrer weakref, j'ai un peu de mal à le comprendre.

y'a t il un rapport avec byref qu'on utilise avec ctypes ?

Je te renvois vers weakref sur python module of the day, qui est tres complet.

Aucun rapport avec le mot-clef byref de ctypes qui lui sert a passer des parametres par référence.

...