IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

Enregistrer la modification des attributs à l'aide d'un descripteur


Sujet :

Python

  1. #1
    Membre éprouvé

    Homme Profil pro
    Ingénieur
    Inscrit en
    Août 2010
    Messages
    654
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2010
    Messages : 654
    Points : 1 150
    Points
    1 150
    Par défaut Enregistrer la modification des attributs à l'aide d'un descripteur
    Bonjour à tous,

    Je suis actuellement en train de m'arracher les cheveux sur la création d'un descripteur.

    Mon problème est le suivant. Je développe un module en python pour un logiciel tiers proposant une jolie API (si on ne regarde pas de trop près). Le logiciel en question fait appel à mon module pour calculer certaines variables au cours de simulations. Le problème c'est que lors d'une simulation je ne peux afficher aucune variable (avec print), ni utiliser un debugger comme pdb. C'est relativement énervant. De plus j'ai besoin de sauvegarder quelque part les valeurs prises par certaines variables au cours de la simulation pour des post-traitements ultérieurs.

    Bref, j'ai essayé quelques méthodes avec plus ou moins de succès (passer par des fichiers textes, utiliser des dictionnaire avec JSON et des "workspaces", etc...). Mais pour chacune de ces méthodes, je suis obligé de modifier mes fonctions/classes afin de « rajouter » l’enregistrement de la variable voulue. Et bien évidemment, ces variables sont ammenées à disparaitre ou de nouvelles à apparaitre.

    J’ai alors pensé aux descripteurs qui je pense impliquent les modifications les plus faibles. Voici mon « Recorder » et un exemple d’utilisation associé. Le changement des attribut de l’objet en question est bien suivi, mais je ne parviens pas à différencier chaque attribut de l’objet… Ce qui est gênant.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    # -*- coding:Utf-8 -*-
     
    from weakref import WeakKeyDictionary
     
    # RECORDS is a dictionary where instances and values will be stored 
    RECORDS = {}
     
    class Recorder(object):
        """A descriptor to track instance changes"""
        def __init__(self, default=None):
            self.data = WeakKeyDictionary()
            self.default = default
     
        def __get__(self, instance, owner):
            if instance is None:
                return self
            return self.data.get(instance, self.default)
     
        def __set__(self, instance, value):
            if instance in RECORDS.keys():
                RECORDS[instance].append(value)
            else:
                RECORDS[instance] = [value]        
            self.data[instance] = value
     
     
    class MyObject(object):
        """Dummy object"""
     
        an_attribute = Recorder(0.0)
        another_attribute = Recorder(0.0)
     
        def __init__(self, name):
            self.name = name
     
        def __repr__(self):
            return self.name
     
     
    if __name__ == '__main__':
     
        # Instanciation of the objects
        an_object = MyObject('an_object')
        another_object = MyObject('another_object')
     
        # Modification of the attributes
        an_object.an_attribute = 1.0
        another_object.an_attribute = 2.0
        another_object.an_attribute = 3.0
        another_object.another_attribute = 4.0
     
        # Display of the records
        print RECORDS
     
    >>> {another_object: [2.0, 3.0, 4.0], an_object: [1.0]}
    Est-ce que l’un de vous pourrais pointer du doigt là où il y a une erreur de raisonnement dans mon code ?

    PS: Oui c'est sale de modifier le dico RECORDS comme ça...

    J

  2. #2
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 685
    Points : 30 974
    Points
    30 974
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Julien N Voir le message
    Mais pour chacune de ces méthodes, je suis obligé de modifier mes fonctions/classes afin de « rajouter » l’enregistrement de la variable voulue. Et bien évidemment, ces variables sont ammenées à disparaitre ou de nouvelles à apparaitre.
    Bonjour

    Pourquoi ne crées-tu pas, dans chacune de tes classes, une méthode "debug" dédiée à l'affichage de toutes ses variables quelles qu'elles soient ???
    Exemple issu d'un de mes projets
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
     
    # Gestion de l'environnement de l'application
     
    # Compatibilité python versions futures
    from __future__ import print_function		# Instruction "print" devient fct
    #from __future__ import unicode_literals	# Toutes chaines en unicode
    #from __future__ import absolute_import		# Les imports sont en absolu
     
    # Objet pour gérer l'environnement du logiciel
    class cEnv(
    		object):							# Objet hérité
    	"Gestion de l'environnement du logiciel"
     
    	# Variables statiques
    	appli="xxx"								# Nom application
    	version="0.3"							# N° de version
    	release=""								# N° de release
    	modeDevel=True							# En développement (None = Arrêt)
    	langageDefault="fr"						# Langage par défaut
    	maxInstance=0							# Nb max exécutions (0 = infini)
    	maxSubWindow=3							# Nb max profondeurs sous-fenêtres
    	qteMax=999							# Quantité maximale
    	remiseMax=40							# Remise maximale
    	euro=decimal.Decimal("6.55957")				# Prix de l'euro en francs
     
    	# Constructeur objet
    	def __init__(
    			self,							# Instance objet
    			argv):							# Arguments du programme
     
    		# Récupération de l'environnement
    		self.argv=argv
     
    		# Nom de l'application
    		self.name=baseName(argv[0], None)
     
    		# Répertoire de travail
    		self.wdir=dirName(argv[0])
     
    		# Répertoire temporaire
    		self.tmp=tempfile.gettempdir()
    		if self.tmp == self.wdir:
    			self.tmp=os.path.join(self.wdir, "tmp")
    		if not os.path.isdir(self.tmp):
    			raise IOError, "%s n'existe pas ou n'est pas un répertoire" %\
    				self.tmp
     
    		# Uname
    		self.host=socket.gethostname()
    		self.ip=socket.gethostbyname(self.host)
     
    	# __init__()
     
    	# Débugging
    	def debug(
    			self,							# Instance objet
    			*args,						# Liste arguments
    			**kwargs):						# Association arguments
     
    		# Initialisation
    		tab=kwargs.get("tab", 0)
     
    		# Debugging objet
    		return "%s%s%s\n" % (
    			"\t" * tab,
    			repr(self),
    			" - %s" % kwargs.get("titre", ""),
    		)\
    		+ "".join(
    			"%s%s\n" % (
    				"\t" * (tab + 1),
    				item,
    			) for item in (
    				"Appli: [%s] (%s)" % (
    					self.appli,
    					type(self.appli),
    				),
    				"Version: [%s] (%s)" % (
    					self.version,
    					type(self.version),
    				),
    				"Release: [%s] (%s)" % (
    					self.release,
    					type(self.release),
    				),
    				"Mode Devel: [%s] (%s)" % (
    					self.modeDevel,
    					type(self.modeDevel),
    				),
    				"max instance: [%d] (%s)" % (
    					self.maxInstance,
    					type(self.maxInstance),
    				),
    				"max sub-window: [%d] (%s)" % (
    					self.maxSubWindow,
    					type(self.maxSubWindow),
    				),
    				"qté max: [%d] (%s)" % (
    					self.qteMax,
    					type(self.qteMax),
    				),
    				"euro: [%d] (%s)" % (
    					self.euro,
    					type(self.euro),
    				),
    				"Working dir: [%s] (%s)" % (
    					self.wdir,
    					type(self.wdir),
    				),
    				"Name: [%s] (%s)" % (
    					self.name,
    					type(self.name),
    				),
    				"Host: [%s] (%s)" % (
    					self.host,
    					type(self.host),
    				),
    				"IP: [%s] (%s)" % (
    					self.ip,
    					type(self.ip),
    				),
    				"tmp: [%s] (%s)" % (
    					self.tmp,
    					type(self.tmp),
    				),
    				"pid: [%d] (%s)" % (
    					os.getpid(),
    					type(os.getpid()),
    				),
    			)
    		)
    	# debug()
    # class cEnv
     
    # Pour tester le module
    if __name__ == "__main__":
    	# Récupération des éléments nécessaires
    	import sys
     
    	print(cEnv(sys.argv).debug(tab=0))
    # if

    Quand je veux vérifier son contenu, j'appelle debug() avec éventuellement un nombre de tabulations à afficher. Et si d'aventure il m'arrivait de rajouter un attribut à mon objet, je n'aurais qu'à modifier la méthode debug() pour afficher sa valeur...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  3. #3
    Membre émérite
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2010
    Messages
    553
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2010
    Messages : 553
    Points : 2 740
    Points
    2 740
    Par défaut
    Salut,

    je saurais pas te dire pourquoi ta méthode ne fonctione, j'ai pas compris...

    par contre, comme le sujet m'a paru interessant, j'ai cherché à faire un truc qui fait ce que tu veux sans trop de code et je suis finalement arrivé à ça:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    # -*- coding:Utf-8 -*-
     
    class Recorder(object):
        """A descriptor to track instance changes"""
     
        records = {}
     
        def __init__(self, name=""):
            self.name = name
            self.history = {}
            Recorder.records[self] = self.history
     
        def __setattr__(self, attr, value):
            super(Recorder, self).__setattr__(attr, value)
            if attr not in ["history", "name"]:
                if attr not in self.history:
                    self.history[attr] = []
                self.history[attr].append(value)
     
        def __repr__(self):
            return self.name
     
     
    class MyObject(Recorder):
        """Dummy object"""
     
        def __init__(self, name):
            super(MyObject, self).__init__(name)
            self.name = name
            self.var1 = 0
            self.var2 = 1.0
            self.var3 = "abc"
     
     
    if __name__ == '__main__':
     
        obj1 = MyObject("obj1")
        obj2 = MyObject("obj2")
     
        obj1.var1 = 8
        obj1.var2 = 1.7
        obj1.var2 = 9.8154
        obj2.var1 = 5
        obj2.var3 = "pouet"
        obj2.var1 = 12
        obj2.var3 = "toto"
     
    print Recorder.records
    #{obj1: {'var1': [0, 8], 'var3': ['abc'], 'var2': [1.0, 1.7, 9.8154]}, obj2: {'var1': [0, 5, 12], 'var3': ['abc', 'pouet', 'toto'], 'var2': [1.0]}}
    en espérant que ça aide...

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 283
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 283
    Points : 36 770
    Points
    36 770
    Par défaut
    Salut,

    Je ne suis pas sûr d'avoir compris ce que vous cherchiez à faire.

    De toutes façons, votre méthode __set__ stocke la "value" pour une instance donnée et quelque soit le nombre quelque soit le nombre d'attributs de type Recorder...
    Si vous en voulez "autant" que, il va falloir les différencier.
    A la base çà commence par une association de l'attribut de classe à son descriptor spécifique
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    class MyObject(object): 
       an_attribute = Recorder('an_attribute', 0.0)
        another_attribute = Recorder('another_attribute', 0.0)
    => RECORDS sera à 2 niveaux: "instance" => "nom" => valeur
    Les "property" fonctionnent pareil:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class A(object):
            _value = None
            @property
            def value(self):
                 if self._value is None:
                    self._value = ...
                 return self._value
    "property" utilise la précédence du __dict__ de la "class" sur celui de l'instance pour avoir des valeurs spécifiques à chaque instance. Si vous voulez stocker la même hiérarchie dans RECORDS i.e. à l'extérieur de class/instance, il faut décomposer mais c'est le même boulot.

    Pour le reste, j'ai l'impression que vous cherchez à réaliser différents jeux de valeurs et avoir la possibilité d'associer vos instances a un jeu particulier, puis à un autre...
    Si c'est le cas, les descriptors permettent d'écrire de façon élégante une partie du problème mais une partie seulement: les attributs d'un jeu sont des "objets", ces "objets" ne seront pas déréférencés automagiquement lorsque vous changez le "jeu" => il faut un eventing pour "signaler" le changement et effectuer les mises à jour.
    Vous vous retrouvez donc dans un style model (les données) et pleins de "views".
    Dans le cas général, c'est difficile mais les cas particuliers sont (heureusement) plus simples.
    Par exemple, un "jeu" <=> un fichier JSON ou pickle contenant l'état de l'ensemble de vos instances.
    Charger le fichier (et les attributs) est un point de synchronisation "global" plutôt simple.

    Mais, il faut prendre le temps de poser le problème et de simplifier plutot que de partir à coder comme un fou en espérant qu'un peu de magie pourra résoudre le problème que vous n'avez pas pris le temps de poser.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  5. #5
    Membre éprouvé

    Homme Profil pro
    Ingénieur
    Inscrit en
    Août 2010
    Messages
    654
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2010
    Messages : 654
    Points : 1 150
    Points
    1 150
    Par défaut
    Bonsoir,

    Tout d'abord merci à vous trois.

    Sve@r, ta solution suppose de créer une méthode supplémentaire à l'objet tracké. Le problème c'est que je souhaite "suivre" plusieurs objet différents (pas seulement des instances différentes). Je pourrais certainement m'en sortir quand même avec ta solution.

    Tryph, je pense que je ne vais pas m'orienter vers de l'héritage. C'est une trop grosse modification de mes classes. De plus, j'avais dans l'esprit de suivre la modification que de certains attributs. Mais je ne sais pas si c'est parce que j'ai eu le temps de me reposer ou, plus probable, c'est ton code qui m'a inspiré. Une simple modification du descripteur acceptant comme attribut une nom permet de différencier aisément les différents attributs suivit d'un même objet. Il suffit tout comme toi et Wiztricks l'avez suggéré, de créer un niveau supplémentaire dans RECORDS.

    Wiztricks, j'avoue que même si je comprends le mécanisme général des descripteurs (en gros "by-passer" la modification ou la récupération d'un attribut afin de rajouter une opération supplémentaire), je ne comprends pas toute la mécanique sous-jacente. J'essaye de faire au mieux de ce que je peux faire. Je compte passer par JSON pour charger et enregistrer mon dictionnaire RECORDS. Le logiciel tiers que j'utilise créé une instance de mon module pour chaque variable calculée par celui-ci.... Le calcul des variable étant strictement identique d'une instance à l'autre, si je ne "partage" pas les résultats entre elles je perds un temps non négligeable.

    Bref, ceci fait l'affaire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    # -*- coding:Utf-8 -*-
     
    from weakref import WeakKeyDictionary
     
    # RECORDS is a dictionary where instances and values will be stored 
    RECORDS = {}
     
    class Recorder(object):
        """A descriptor to track instance changes"""
        def __init__(self, name, default=None):
            self.data = WeakKeyDictionary()
            self.default = default
            self.name = name
     
        def __get__(self, instance, owner):
            if instance is None:
                return self
            return self.data.get(instance, self.default)
     
        def __set__(self, instance, value):
            if instance in RECORDS.keys():
                if self.name in RECORDS[instance].keys():
                    RECORDS[instance][self.name].append(value)
                else:
                    RECORDS[instance][self.name] = [value]
            else:
                RECORDS[instance] = {self.name: [value]}
     
     
    class MyObject(object):
        """Dummy object"""
     
        var1 = Recorder('var1', 0)
        var2 = Recorder('var2', 1.0)
     
        def __init__(self, name):
            self.name = name
            self.var3 = "abc"
     
        def __repr__(self):
            return self.name
     
     
    if __name__ == '__main__':
     
        obj1 = MyObject("obj1")
        obj2 = MyObject("obj2")
     
        obj1.var1 = 8
        obj1.var2 = 1.7
        obj1.var2 = 9.8154
        obj2.var1 = 5
        obj2.var3 = "pouet"
        obj2.var1 = 12
        obj2.var3 = "toto"
     
        print RECORDS
     
        >>> {obj2: {'var1': [5, 12]}, obj1: {'var1': [8], 'var2': [1.7, 9.8154]}}
    Si vous avez des critiques, je suis ouvert. Comme par exemple rajouter le chargement et l'enregistrement de RECORDS directement dans le descripteurs. Sinon je passerais en résolu.

    Une dernière chose Wiztricks, est-ce que vous pourriez m'en dire plus sur le "déférencement" que vous évoquez?
    Si c'est le cas, les descriptors permettent d'écrire de façon élégante une partie du problème mais une partie seulement: les attributs d'un jeu sont des "objets", ces "objets" ne seront pas déréférencés automagiquement lorsque vous changez le "jeu" => il faut un eventing pour "signaler" le changement et effectuer les mises à jour.
    Ciao ciao

  6. #6
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 685
    Points : 30 974
    Points
    30 974
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Julien N Voir le message
    Sve@r, ta solution suppose de créer une méthode supplémentaire à l'objet tracké. Le problème c'est que je souhaite "suivre" plusieurs objet différents (pas seulement des instances différentes).
    C'est ce que je fais avec tous mes objets importants. Tous ont une méthode "debug" faite de la même manière. Et certaines méthodes de certains objets qui utilisent eux-mêmes d'autres objets appellent elles-mêmes les debug de ces objets.
    Voici d'ailleurs un exemple d'un tel debug...
    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    class cEnv(object):
    	... (tout le reste)...
    
    	# Débugging
    	def debug(
    			self,							# Instance objet
    			*args,							# Liste arguments
    			**kwargs):						# Association arguments
    
    		# Initialisation
    		tab=kwargs.get("tab", 0)
    
    		# Debugging objet
    		return "%s%s%s\n" % (
    			"\t" * tab,
    			repr(self),
    			" - %s" % kwargs.get("titre", ""),
    		)\
    		+ "".join(
    			"%s%s\n" % (
    				"\t" * (tab + 1),
    				item,
    			) for item in (
    				"Appli: [%s] (%s)" % (
    					self.appli,
    					type(self.appli),
    				),
    				"Version: [%s] (%s)" % (
    					self.version,
    					type(self.version),
    				),
    				"pid: [%d] (%s)" % (
    					os.getpid(),
    					type(os.getpid()),
    				),
    			)
    		)\
    		+ "".join(
    			item.debug(tab=tab + 1, titre=titre)
    			for (item, titre) in (
    				(self.config, "config"),
    				(self.xxx, "xxx"),
    				(self.yyy, "yyy"),
    			)
    		)
    	# debug()
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  7. #7
    Membre émérite
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2010
    Messages
    553
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2010
    Messages : 553
    Points : 2 740
    Points
    2 740
    Par défaut
    Citation Envoyé par Julien N Voir le message
    Si vous avez des critiques, je suis ouvert. Comme par exemple rajouter le chargement et l'enregistrement de RECORDS directement dans le descripteurs. Sinon je passerais en résolu.
    perso le truc qui me gène dans ton code (et dont tu as conscience si on se fie à ton premier post) c'est cette variable RECORDS.
    tu peux en faire une variable de classe Recorder et y accéder de n'importe ou avec Recorder.RECORDS, c'est que j'ai fait dans mon bout de code.

  8. #8
    Membre éprouvé

    Homme Profil pro
    Ingénieur
    Inscrit en
    Août 2010
    Messages
    654
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2010
    Messages : 654
    Points : 1 150
    Points
    1 150
    Par défaut
    Salut,

    Sve@r, moi aussi je définis une method "debug" pour certains de mes programmes. Jusqu'à maintenant, mon "recorder" fonctionnait sous le meme principe. J'appelais une methode chargeant un dictionnaire avec JSON et enregistrant les nouvelles valeurs de certains attributs. ça marche très bien. Mais je me suis dit pourquoi pas generaliser cela pour plusieurs classes, mais avec une seule function sans pour autant modifier les classes en question. Ici je ne modifie plus que la declaration des attributs, je n'ai pas besoin de faire appel à un moment ou un autre à la methode debug().

    Tryph, oui comme je le disait, RECORDS est sale. Je l'ai enlevé au profit d'un attribut de Recorder:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    from weakref import WeakKeyDictionary
     
    class Recorder(object):
        """A descriptor to track instance changes"""
     
        # records is a dictionary where instances and values will be stored
        records = {}
     
        def __init__(self, name, default=None):
            self.data = WeakKeyDictionary()
            self.default = default
            self.name = name
     
        def __get__(self, instance, owner):
            if instance is None:
                return self
            return self.data.get(instance, self.default)
     
        def __set__(self, instance, value):
            if instance in self.records.keys():
                if self.name in self.records[instance].keys():
                    self.records[instance][self.name].append(value)
                else:
                    self.records[instance][self.name] = [value]
            else:
                self.records[instance] = {self.name: [value]}
            self.data[instance] = value
    A cela je rajouterais le chargement du dico records depuis un fichier texte avec JSON.

    Merci à tous.


    Ju

  9. #9
    Membre éclairé
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    625
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 625
    Points : 822
    Points
    822
    Par défaut
    Petit bémol à cette méthode.

    Les variables enregistrées par les descripteurs ne sont plus disponibles dans le dict de la classe qui les utilise : "vars(monInstance)" ou "monInstance.__dict__" renvoient un résultat incorrect. Attention donc si cela peut avoir une incidence sur le reste de ton code.
    Pourfendeur de singletons en croisade

  10. #10
    Membre éprouvé

    Homme Profil pro
    Ingénieur
    Inscrit en
    Août 2010
    Messages
    654
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2010
    Messages : 654
    Points : 1 150
    Points
    1 150
    Par défaut
    Merci Petibidon, je n'en savais rien. Je ne pense pas que cela soit genant dans mon cas.

  11. #11
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 283
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 283
    Points : 36 770
    Points
    36 770
    Par défaut
    Salut,

    La gestion de self.data est a revoir: çà ne retourne pas la valeur courante.

    Une façon de remettre RECORDS dans la hiérarchie de class/instance est de coller un dict à chaque instance.
    Ca donne un code du genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    class recorder(object):
        def __init__(self, name, default=None):
            self._name = name
            self._default = default
     
        def __get__(self, instance, klass):
            if not instance:
                return self
            ns = instance.__dict__
            return ns.get(self._name, self._default)
     
        def __set__(self, instance, value):
            name = self._name
            ns = instance.__dict__
            old_value = ns.get(name, self._default)
            if name in instance._records:
                instance._records[name].append(old_value)
            else:
                instance._records[name] = [ old_value ]
            ns[name] = value        
     
     
    class MyObject(object):
        _instances = {}
     
        var1 = recorder('var1', 0)
        var2 = recorder('var2', 1.0)
     
        def __new__(cls, name):
            e = cls._instances.get(name)
            if not e:
                e = cls._instances[name] = super(MyObject, cls).__new__(cls)
                e._records = {}
            return e            
        def __init__(self, name):
            if not hasattr(self, name):
                self.name = name
     
        def __repr__(self):
            return self.name
     
        @classmethod
        def dumps(cls):
            for name, e in cls._instances.items():
                print (name, e._records)
     
    if __name__ == '__main__':
     
     
        obj1 = MyObject("obj1")
        obj2 = MyObject("obj2")
     
        obj1.var1 = 8
        obj1.var2 = 1.7
        obj1.var2 = 9.8154
        print obj1.var2
        obj2.var1 = 5
        obj2.var3 = "pouet"
        obj2.var1 = 12
        obj2.var3 = "toto"
     
        MyObject.dumps()
    - W
    edit: corrigé quelques oops.
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  12. #12
    Membre éprouvé

    Homme Profil pro
    Ingénieur
    Inscrit en
    Août 2010
    Messages
    654
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2010
    Messages : 654
    Points : 1 150
    Points
    1 150
    Par défaut
    Salut,

    Citation Envoyé par wiztricks Voir le message
    La gestion de self.data est a revoir: çà ne retourne pas la valeur courante.
    Merci pour cette solution. Elle est beaucoup plus complète. Mais je ne comprends pas pourquoi la valeur courante n'est pas retournée. Je n'ai pas note sur les quelques tests effectués de problème avec get, la valeur de l'attribut est toujours retourné correctement.


    J

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Modifications des attributs d'une classe
    Par Lolitaaa dans le forum Débuter avec Java
    Réponses: 1
    Dernier message: 10/12/2009, 10h27
  2. Réponses: 1
    Dernier message: 07/05/2007, 16h12
  3. Modification des attributs d'un TComPort
    Par Reven777 dans le forum C++Builder
    Réponses: 3
    Dernier message: 28/06/2006, 11h16
  4. Modif des attributs d'une balise <param> en javascript
    Par Henri dans le forum Général JavaScript
    Réponses: 1
    Dernier message: 06/08/2005, 22h20

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo