connaitre les instances d'une classe - Python - Programmation
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...
Marsh Posté le 12-06-2010 à 13:46:21
Vous allez buter le GC avec vos conneries, utilisez des weakrefs.
Marsh Posté le 12-06-2010 à 13:47:16
Sve@r a écrit : |
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
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 :
|
Résultat
Code :
|
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....
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 :
|
Ç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 :
|
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 :
|
Code :
|
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 :
|
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 :
|
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 :
|
Code :
|
Questions?
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): |
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. |
1. ouai je vais utiliser un attribut de classe ou weakref si je comprend quelque chose
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.
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
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.
Marsh Posté le 12-06-2010 à 21:34:11
masklinn a écrit : |
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
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 édité par Profil supprimé le 12-06-2010 à 02:40:58