connaitre les instances d'une classe

connaitre les instances d'une classe - Python - Programmation

Marsh Posté le 12-06-2010 à 02:40:28    

Plop,
 
y'a t'il une possiblité de connaitre les instances d'une classe, un peu comme avec __subclasses__()
 
pour le moment, j'ai une liste globale (enfin pas vraiment globale mais en dehors de la classe) qui stocke les instances
 

instances = []
class Plop(object):
    def __init__(self):
        instances.append(self)


 
Je peux également, faire çà?
 

class Plop(object):
    instances = []
    def __init__(self):
        Plop.instances.append(self)


 
c'est quoi le plus mieux?
__subclasses__ ca me plait bien, mais j'ai rien trouvé d'équivalent pour les instances

Message cité 1 fois
Message édité par Profil supprimé le 12-06-2010 à 02:40:58
Reply

Marsh Posté le 12-06-2010 à 02:40:28   

Reply

Marsh Posté le 12-06-2010 à 09:08:37    


Pas à ma connaissance. C'est un peu comme si tu demandais la liste de tes variables. T'es quand même le programmeur; tu dois connaitre les variables que t'as créé !!
 
 
Pas super propre les globales...
 
 
Beaucoup mieux. Ton tableau est intégré à la classe ce qui l'associe quoi...


---------------
Vous ne pouvez pas apporter la prospérité au pauvre en la retirant au riche.
Reply

Marsh Posté le 12-06-2010 à 13:46:21    

Vous allez buter le GC avec vos conneries, utilisez des weakrefs.


---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 12-06-2010 à 13:47:16    

Sve@r a écrit :


Pas à ma connaissance. C'est un peu comme si tu demandais la liste de tes variables. T'es quand même le programmeur; tu dois connaitre les variables que t'as créé !!


Oui enfin si c'est une lib que tu écris, on ne peut pas dire que tu ais crée les instances de la classe que tu as écrite :)


---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 12-06-2010 à 19:12:48    

Bonjour,
 
 
Question intéressante.
je n’ai pas non plus trouvé de fonction comme __subclasses__()
 
 
 
Une liste instances comme attribut de classe , je trouve aussi que c’est mieux: on est sûr en appelant Plop.instances ou p.instances (avec p une instance de Plop) de ne trouver dans l’objet que des instances de la classe Plop.
 
 
 
Mais je suis allé plus loin à partir de la réflexion suivante:  
si on veut connaître les instances créées d’une classe, c'est pour en faire quelque chose.
Si, si. Je le crois  ;)  
 
 
 
La première idée, c’est que c'est sans doute pour connaître les valeurs des attributs.
Je me suis lancé dans le codage d’un petit programme  
... et ça a été plus long que prévu:  
je me suis aperçu qu’il n’était pas du tout facile de trouver le nom d’une instance donnée.
Pour cela, une fonction définie dans la classe n’est pas suffisante, il faut prévoir l’objectif avec des instructions placées dans __init__() . Et il faut passer par une interrogation de globals() .  
Et de plus au moment où une instance est créée, le nom de l’instance n’est pas encore dans globals() , on ne peut donc pas l’enregistrer dans la liste instances :
cela oblige à reporter au moment où la fonction est appelée la recherche des noms des instances de la classe concernée par interrogation de globals() .
Du moins je n’ai trouvé que ça comme solution.
 
 
 
 
Je me suis dit aussi qu’il pourrait être nécessaire dans certains cas de connaître l’ordre dans lequel les instances ont été créées. J’ai mis le numéro d’ordre de création dans la variable de classe numero.
 
 
 
 
Enfin, pour afficher les résultats sous une forme prédéfinie, j’ai créé une fonction dans la classe, qu’il suffit d’appeler.
Pour pouvoir l’appeler aussi bien à partir d’une instance que de la classe, j’ai fait de cette fonction une méthode de classe et non pas seulement une méthode d’instance.
 
 
 

Code :
  1. class B:
  2.     a = '~~~~'
  3.     b = '~~~'
  4.     numero = 0
  5.     instances = {}
  6.     def __init__(self,x=0,ch=''):
  7.         self.y = 3*x
  8.         if ch=='NOIR':
  9.             self.bla = 'NOIR'
  10.         elif len(ch)>2:
  11.             self.bla = 'rouge'
  12.         elif len(ch)==1:
  13.             self.bla = 'blanc'
  14.         else:
  15.             self.bla = '--^--'
  16.            
  17.         self.__class__.numero += 1
  18.         if self.numero==1:
  19.             B.a = 'soleil'
  20.             B.b = 'lune'
  21.             num = '1ere'
  22.         else:
  23.             num = str(self.numero)+'ere'
  24.         self.instances[repr(self)] = num
  25.        
  26.     @classmethod
  27.     def les_instances(cls):
  28.         print('\n======================================='\
  29.               +'\nINSTANCES DE LA CLASSE DE NOM :  '+cls.__name__\
  30.               +'\n  - Attributs de la classe:'\
  31.               +'\n    a = '+repr(cls.a)\
  32.               +'\n    b = '+repr(cls.b)+'\n')
  33.         if cls.instances:
  34.             sortie = []
  35.             for obj_name,obj in globals().items():
  36.                 R = repr(globals()[obj_name])
  37.                 if R in cls.instances:
  38.                     sortie.append('\n'.join(('  * '+cls.instances[R]\
  39.                                             + ' instance creee de la classe '+cls.__name__,
  40.                                             "    - Nom de l'instance:  "+repr(obj_name),
  41.                                             "    - Attributs de l'instance:",
  42.                                             '      y = '+repr(obj.y),
  43.                                             '      bla = '+repr(obj.bla),
  44.                                             '')))
  45.             sortie.sort()
  46.             print('\n'.join(sortie)+'\n---------------------------------------')
  47.         else:
  48.             print("  - Aucune instance de la classe "+cls.__name__+" n'a encore ete creee.\n"\
  49.                   +'\n--------------------------------------------------------')
  50.            
  51.     def combine(self,what=0):
  52.         if what:
  53.             self.bla = B.a + ' ///// '+  self.bla +' ::: '+str(what)
  54.         else:
  55.             self.bla = B.b +'  '+ self.bla
  56.             B.b = '@@@ Pluton @@@'
  57.            
  58. # execution     
  59. B.les_instances()
  60. print('creation des instances :\nmassive, invisible, galaxie et quoiquoi')
  61. massive = B(100,'jupiter')
  62. invisible = B(-80)
  63. galaxie = B(0.2,'Andromede')
  64. quoiquoi  = B(ch='kilo')
  65. B.les_instances()
  66. print('execution de :\ninvisible.combine(3)\nmassive.combine(0)')
  67. invisible.combine(3)
  68. massive.combine(0)
  69. B.les_instances()


 
 
Résultat
 
 

Code :
  1. =======================================
  2. INSTANCES DE LA CLASSE DE NOM :  B
  3.   - Attributs de la classe:
  4.     a = '~~~~'
  5.     b = '~~~'
  6.   - Aucune instance de la classe B n'a encore ete creee.
  7. --------------------------------------------------------
  8. creation des instances :
  9. massive, invisible, galaxie et quoiquoi
  10. =======================================
  11. INSTANCES DE LA CLASSE DE NOM :  B
  12.   - Attributs de la classe:
  13.     a = 'soleil'
  14.     b = 'lune'
  15.   * 1ere instance creee de la classe B
  16.     - Nom de l'instance:  'massive'
  17.     - Attributs de l'instance:
  18.       y = 300
  19.       bla = 'rouge'
  20.   * 2ere instance creee de la classe B
  21.     - Nom de l'instance:  'invisible'
  22.     - Attributs de l'instance:
  23.       y = -240
  24.       bla = '--^--'
  25.   * 3ere instance creee de la classe B
  26.     - Nom de l'instance:  'galaxie'
  27.     - Attributs de l'instance:
  28.       y = 0.6000000000000001
  29.       bla = 'rouge'
  30.   * 4ere instance creee de la classe B
  31.     - Nom de l'instance:  'quoiquoi'
  32.     - Attributs de l'instance:
  33.       y = 0
  34.       bla = 'rouge'
  35. ---------------------------------------
  36. execution de :
  37. invisible.combine(3)
  38. massive.combine(0)
  39. =======================================
  40. INSTANCES DE LA CLASSE DE NOM :  B
  41.   - Attributs de la classe:
  42.     a = 'soleil'
  43.     b = '@@@ Pluton @@@'
  44.   * 1ere instance creee de la classe B
  45.     - Nom de l'instance:  'massive'
  46.     - Attributs de l'instance:
  47.       y = 300
  48.       bla = 'lune  rouge'
  49.   * 2ere instance creee de la classe B
  50.     - Nom de l'instance:  'invisible'
  51.     - Attributs de l'instance:
  52.       y = -240
  53.       bla = 'soleil ///// --^-- ::: 3'
  54.   * 3ere instance creee de la classe B
  55.     - Nom de l'instance:  'galaxie'
  56.     - Attributs de l'instance:
  57.       y = 0.6000000000000001
  58.       bla = 'rouge'
  59.   * 4ere instance creee de la classe B
  60.     - Nom de l'instance:  'quoiquoi'
  61.     - Attributs de l'instance:
  62.       y = 0
  63.       bla = 'rouge'
  64. ---------------------------------------


 
 
 
La remarque de masklinn est intéressante mais je n’en saisis pas toute la portée. Qu’est ce que tu entends par « buter le GC », stp masklinn ?
 
NB :Je sais que GC = garbage collector , quand même....

Reply

Marsh Posté le 12-06-2010 à 20:12:21    

eyquem a écrit :

La remarque de masklinn est intéressante mais je n’en saisis pas toute la portée. Qu’est ce que tu entends par « buter le GC », stp masklinn ?


1. Dans son implémentation, Ekxon conserve ses instances dans une liste classique. Mettre un objet dans une liste, ça veut dire créer une référence vers cet objet
2. Le GC ne collecte que les objets qui ne sont plus référencés
3. La durée de vie d'une classe est jusqu'à ce que l'interpréteur soit coupé

 

Le résultat, c'est qu'ici la classe va garder une référence vers chacune de ses instance et donc empêcher que le GC ne récupère la mémoire associée pour toujours. Et si plus le temps passe et plus tu crées d'instances (genre dans un server), tu as un memleak de qualité.

 

La solution, c'est un mécanisme appelé "weak references". En gros, c'est une référence vers la référence, ça permet de récupérer la référence (et donc l'objet associé) mais ça n'est pas une référence complète, et ça n'empêche donc pas le GC de collecter les objets weak-referenced.

 

Après, il y a plusieurs manières de jouer, qui tendent à faire chier à l'accès, à la création ou avant même la création

 

weakref.ref et weakref.proxy
Ces méthodes sont extrèmement simples à créer: au lieu de mettre un objet dans la liste, on met une ref ou un proxy

Code :
  1. class Plop(object):
  2.    instances = []
  3.    def __init__(self):
  4.        Plop.instances.append(weakref.ref(self))


Ça pourrait difficilement être plus simple, mais la liste n'est pas au courant qu'elle contient des weakrefs, donc même quand les objets sont collectés, les refs/proxies restent dans la liste (donc il faut vérifier à chaque accès, et potentiellement faire du nettoyage):

Code :
  1. for ref in Plop.instances:
  2.    instance = ref()
  3.    # on ne peut pas faire le nettoyage ici, vu qu'on est en train d'itérer sur la liste
  4.    if instance is not None:
  5.        # do stuff
  6.  
  7. # alternative avec nettoyage de la liste:
  8. for ref in Plop.instances[:]: # on copie toute la liste et on itère sur la copie
  9.    instance = ref()
  10.    if instance is None:
  11.        Plop.instances.remove(ref)
  12.        continue
  13.    # do stuff


 Le résultat est que le proxy est probablement contre-indiqué: il n'y a pas moyen de savoir s'il y a encore un objet derrière. Avec une ref, il faut appeler la ref et ça renvoie None s'il n'y a plus rien derrière, donc à l'accès il est possible de vérifier les refs et même de faire du nettoyage des refs n'existant plus.

 

Donc fort coût à l'accès et chances de bugs

 

WeakValueDictionary
La 2e méthodes est beaucoup plus simple à l'accès (mais pas idéale) mais plus lourde à la création: weakref contient un dict dont les clés sont des weakrefs et un dict dont les valeurs sont des weakrefs. Les deux s'utilisent comme un dict classique (on leur donne des objets, pas des weakrefs) et si l'objet derrière la weakref (clé ou valeur) disparaît, la weakref est supprimée du dict.

 

Donc on fait de Plop.instances un WeakValueDictionary, on met une clé à la con genre l'id de l'object, et quand on veut accéder aux instances on passe par Plop.instances.values() au lieu de plop.instances

Code :
  1. class Plop(object):
  2.    instances = weakref.WeakValueDictionary()
  3.    def __init__(self):
  4.        Plop.instances[id(self)] = self


Code :
  1. for instance in Plop.instances.values():
  2.    # on récupère uniquement des instances vivantes, rien de plus à faire
  3.    # do stuff
 

Callbacks
La 3e méthodes est triviale à l'accès, mais moins lisible que même 2: weakref.ref() et weakref.proxy() peuvent prendre un second paramètre, qui est un callable. Ce callable est appelé avec la ref() ou le proxy() en paramètre quand l'objet qui est derrière est collecté par le GC. Il suffit d'utiliser weakref.proxy et d'enlever le proxy de notre liste quand le callback est appelé, et on a une liste qui semble classique, mais qui s'auto-nettoie quand les instances disparaissent:

 
Code :
  1. class Plop(object):
  2.    instances = []
  3.    def __init__(self):
  4.        Plop.instances.append(weakref.proxy(self, Plop.instances.remove))


Avec un peu de chance ça devrait marcher, ou on pourrait utiliser ref() pour être tranquille (je ne sais pas quels accès sont faits dans remove, il est possible que ça fasse pêter un proxy dans certaines conditions)

Code :
  1. # via proxy, mais je ne certifie pas que ça marche toujours bien
  2. for instance in Plop.instances: # c'est un proxy en fait, mais OSEF
  3.    # on récupère uniquement des instances vivantes, rien de plus à faire
  4.    # do stuff
  5.  
  6. # via refs
  7. for ref in Plop.instances:
  8.    instance = ref()
  9.    # nos refs sont obligatoirement vivantes, pas besoin de checker None
  10.    # do stuff
 

Custom class
La 4e méthodes est naturellement de réimplémenter une liste de la même manière que WeakValueDictionary() réimplémente un dict histoire de garder toute la tambouille de callbacks internes à la collection. C'est la plus longue en terme de travail initial (il faut réimplémenter toutes les méthodes de list()), mais ça sera aussi la plus simple à utiliser, la plus transparente et (une fois débuggée) la plus fiable. On se retrouvera avec le code proposé par Ekxon (enfin avec [] remplacé par un truc genre WeakList), mais sans memory leaks:

Code :
  1. class Plop(object):
  2.    instances = WeakList()
  3.    def __init__(self):
  4.        Plop.instances.append(self)


Code :
  1. for instance in Plop.instances:
  2.    # on récupère des instances vivantes, pas besoin de vérifier quoi que ce soit
  3.    # do stuff
 

Questions?


Message édité par masklinn le 12-06-2010 à 20:22:21

---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 12-06-2010 à 20:51:46    

merci à tous de vous être penchés sur ma question,
 
alors je suis allé regarder la doc de weakref et j'ai pas tout compris, mais avant d'approfondir je voulais expliquer pourquoi je souhaite
garder une liste de mes instances, peut être qu'en fait c'est pas nécessaire.
 
j'ai en gros une classe qui correspond à une sorte de plugin, un composant à part
 

class Component(object):
    def __init__(self, name):
        self.name = name.lower()        
        self.signals = collections.defaultdict(list)
        self.options = {}

ces objets peuvent enregistrer des fonctions à executer sur certains évenements stockés dans self.signals.

{"SIGNAL_1" : [func_a, func_b], "SIGNAL_2" : [func_c]}


 
lorsqu'un évènement se produit, je compte parcourir toutes mes instances de la classe Component et vérifier
si le type de l'évènement est enregistré dans self.signals puis executer la ou les actions,
Plusieurs instances peuvent gérer le même évènement.
 
Je voudrais aussi pouvoir "charger"/"décharger" un composant par son nom, donc je dois connaitre les composants enregistrés,
vérifier qu'il n'en existe pas un avec le même nom. etc.
 
c'est pour çà que je voulais avoir accès à la liste de ces instances, et il y aura surement d'autres raisons par la suite  
 
 
 
 
 
 

eyquem a écrit :

1 Une liste instances comme attribut de classe , je trouve aussi que c’est mieux: on est sûr en appelant Plop.instances ou p.instances (avec p une instance de Plop) de ne trouver dans l’objet que des instances de la classe Plop.
 
2 je me suis aperçu qu’il n’était pas du tout facile de trouver le nom d’une instance donnée.
Pour cela, une fonction définie dans la classe n’est pas suffisante, il faut prévoir l’objectif avec des instructions placées dans __init__() . Et il faut passer par une interrogation de globals() .  
Et de plus au moment où une instance est créée, le nom de l’instance n’est pas encore dans globals() , on ne peut donc pas l’enregistrer dans la liste instances :
cela oblige à reporter au moment où la fonction est appelée la recherche des noms des instances de la classe concernée par interrogation de globals() .
Du moins je n’ai trouvé que ça comme solution.
 
 
3 Je me suis dit aussi qu’il pourrait être nécessaire dans certains cas de connaître l’ordre dans lequel les instances ont été créées. J’ai mis le numéro d’ordre de création dans la variable de classe numero.


 
1. ouai je vais utiliser un attribut de classe ou weakref si je comprend quelque chose  :whistle:  
2. j'ai pas compris cette partie, tu entends quoi par le nom de l'instance? le nom de la variable à laquelle on associe l'instance?
j'initialise la classe avec un nom pour les différencier.
3. mes instances sont stockées dans une liste, pour la priorité c'est en fonction de l'ordre dans lequel je les crée, en revanche les fonctions dans self.signals
elles auront un ordre de priorité, et je voudrais également pouvoir annuler le prolongement d'un signal.

Reply

Marsh Posté le 12-06-2010 à 20:54:37    

@masklinn j'ai pas encore lu ton post, j'ai répondu à Eyquem entre temps, je regarde çà merci :)

Reply

Marsh Posté le 12-06-2010 à 21:02:58    


Pourquoi tu stockes tes trucs dans self.signals plutôt que d'avoir un vrai enregistrement de signaux, complètement indépendant de tes objets?

 

Et pourquoi tu réimplémentes un système de signaux? Il en existe probablement déjà, genre pydispatcher.

Message cité 1 fois
Message édité par masklinn le 12-06-2010 à 21:03:18

---------------
Stick a parrot in a Call of Duty lobby, and you're gonna get a racist parrot. — Cody
Reply

Marsh Posté le 12-06-2010 à 21:34:11    

masklinn a écrit :


Pourquoi tu stockes tes trucs dans self.signals plutôt que d'avoir un vrai enregistrement de signaux, complètement indépendant de tes objets?
 
Et pourquoi tu réimplémentes un système de signaux? Il en existe probablement déjà, genre pydispatcher.


 
ca me paraissait plus simple, chaque plugin une fois chargé déclare ses propres signaux.
puis lorsqu'un signal est émis quelque part dans l'application, je parcours tous les plugins.
si le plugin est supprimé les signaux associés ne sont plus gérés automatiquement
 
avec un gestionnaire de signaux, il faudrait que les plugins ajoutent/suppriment "manuellement" les signaux au chargement/suppresion du plugin.
je suppose qu'il faudrait avoir une "Id" pour chaque évènement/callback
 
je vais regarder pydispatcher même si je préfère éviter d'avoir des dépendances  :jap:  

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed