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 :

bonne pratique - référence d'instances


Sujet :

Python

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 15
    Par défaut bonne pratique - référence d'instances
    Bonjour !

    Je vais dans le cadre d'une application créer des instances à la volée, dont je ne peux prévoir le nombre avant. Le but est de les 'indexer', en tout cas de pouvoir les rappeler plus tard pour agir dessus (par méthodes ...).

    Je pensais au départ retourner l'id de la fonction par la fonction __init__, or celle-ci ne peut rien retourner.

    J'ai alors pensé à instancier par la commande id(Classe()), ce qui retourne effectivement l'id. Je comptais rappeler plus tard cette instance par son id. Cependant, d'après ce que j'ai compris, comme Python détruit ce qui n'est plus référencé, le ramasse-miettes va libérer cette instance à la première occasion.

    Il devient donc apparemment nécessaire d'instancier sous une forme instance = Classe(). Ma question se pose ici : comment choisir la référence ? génération aléatoire ? Existe t-il des bonnes pratiques en la matière ?

    Merci de votre avis

  2. #2
    Membre émérite

    Profil pro
    Inscrit en
    Août 2004
    Messages
    723
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 723
    Par défaut
    S'il s'agit de créer des instances à la volée et de garder une référence dessus, il te suffit de les garder dans une liste stockée comme variable de classe :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    >>> class A:
    ...     instances = []
    ...     def __init__(self):
    ...         # Constructeur habituel
    ...         A.instances.append(self)
    ... 
    >>> inst = [A() for i in range(10)]
    >>> inst
    [<__main__.A instance at 0x8661bec>, <__main__.A instance at 0x8661b4c>, <__main__.A instance at 0x8661d6c>, <__main__.A instance at 0x8661d8c>, <__main__.A instance at 0x8661dac>, <__main__.A instance at 0x8661dcc>, <__main__.A instance at 0x8661dec>, <__main__.A instance at 0x8661e2c>, <__main__.A instance at 0x8661e4c>, <__main__.A instance at 0x8661e6c>]
    >>> A.instances
    [<__main__.A instance at 0x8661bec>, <__main__.A instance at 0x8661b4c>, <__main__.A instance at 0x8661d6c>, <__main__.A instance at 0x8661d8c>, <__main__.A instance at 0x8661dac>, <__main__.A instance at 0x8661dcc>, <__main__.A instance at 0x8661dec>, <__main__.A instance at 0x8661e2c>, <__main__.A instance at 0x8661e4c>, <__main__.A instance at 0x8661e6c>]

  3. #3
    Membre émérite
    Homme Profil pro
    Inscrit en
    Décembre 2007
    Messages
    758
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Décembre 2007
    Messages : 758
    Par défaut
    Bonjour,

    Question interessante J'ai été confronté à ce type de problème et j'ai choisi de créer un registre externe à la classe sous forme d'un WeakValueDictionary du module weakref et j'ai une métaclasse utilisée par tous mes objets qui inscrit les instances dans ce registre. Ce registre est commun à tous les types d'objets.

    Je ne sais pas si ce type de conception est une "bonne pratique", aussi je suis vraiment curieux de voir les réponses à ce topic.

  4. #4
    Expert confirmé
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 486
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 486
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    En ce qui me concerne, j'utilise dans ce cas une liste qui porte les instances de classe: c'est simple et ça marche très bien:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    L = []
     
    # création des instances
    L.append(A())
    L.append(A())
     
    # appel des instances:
    x = L[0].attributdeclasse
    L[1].methodedeclasse(a, b, c)
     
    # Balayage de toutes les instances:
    for l in L:
        z = l.methodedeclasse(u,v)
    On utilise ça aussi avec les classes thread (module threading) quand on ne sait pas combien il y aura d'instances, ou que leur nombre varie constamment.

    Tyrtamos

  5. #5
    Membre émérite
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Par défaut
    J'avais exposé une des solutions possibles dans un précédent post:
    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
    >>> class Collector(list):
    ...     def __call__(self, init):
    ...         def newInit(itself, *args, **kwargs):
    ...             init(itself, *args, **kwargs)
    ...             self.append(itself)
    ...
    ...         return newInit
    ...
    >>> collector = Collector()
    >>>
    >>> class MyTrackedClass(object):
    ...     @collector
    ...     def __init__(self):
    ...         pass
    ...
    >>> MyTrackedClass()
    <__main__.MyTrackedClass object at 0x0231E550>
    >>> MyTrackedClass()
    <__main__.MyTrackedClass object at 0x0231E430>
    >>> MyTrackedClass()
    <__main__.MyTrackedClass object at 0x0231E710>
    >>> MyTrackedClass()
    <__main__.MyTrackedClass object at 0x0231E730>
    >>> collector
    [<__main__.MyTrackedClass object at 0x0231E550>, <__main__.MyTrackedClass object
     at 0x0231E430>, <__main__.MyTrackedClass object at 0x0231E710>, <__main__.MyTra
    ckedClass object at 0x0231E730>]
    Avec ce code, peu importe où et comment tu crées l'objet, si l'instanciation a réussi, alors cet objet sera listé dans collector.
    Tu peux bien sûr mettre des weakref dans collector si ça te chante.

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 15
    Par défaut
    Intéressant d'avoir plusieurs réponses différentes

    Pour Antoine_935 : si on utilise les weakref, comment peut-on alors faire pour revenir chercher dans le collector l'objet que l'on souhaite ? Par exemple, si un des objets placé avant dans la liste a été détruit car n'est plus utilisé ailleurs, tout va être décalé d'un cran non ? Ou alors est-ce qu'on obtient une liste du type [Objet1,Objet2,void,Objet3] ?

  7. #7
    Membre émérite
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Par défaut
    La weakref restera dans la liste où elle était: seul un ordre explicite sur la liste peut l'en retirer.
    Il y a dans la weakref, par contre, un moyen de savoir si l'objet existe toujours ou non: il suffit d'appeler la weakref. Si ça retourne None, l'objet est mort.

    Voici un exemple.
    Note intéressante, et à laquelle je ne m'attendais pas: lors de mon premier test w() is None, j'avais déjà perdu la référence à l'objet référencé. Pourtant, le garbage collector ne l'avait visiblement pas encore détruit.
    Attention donc, puisque ça peut poser des problèmes dans certains cas.
    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
    >>> import weakref
    >>> class MyClass:
    ...     pass
    ...
    >>> m = MyClass()
    >>> w = weakref.ref(m)
    >>> w()
    <__main__.MyClass instance at 0x0232B7B0>
    >>> m = MyClass()
    >>> w() is None
    False
    >>> w
    <weakref at 02329660; dead>
    >>> w()
    >>> w() is None
    True

  8. #8
    Membre Expert
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Par défaut
    Note intéressante, et à laquelle je ne m'attendais pas: lors de mon premier test w() is None, j'avais déjà perdu la référence à l'objet référencé. Pourtant, le garbage collector ne l'avait visiblement pas encore détruit.


    À la première lecture, il m'avait semblé comprendre. Et maintenant je ne comprends plus.

    Le premier test w() is None est bien là ? :
    >>> import weakref
    >>> class MyClass:
    ... pass
    ...
    >>> m = MyClass()
    >>> w = weakref.ref(m)
    >>> w()
    <__main__.MyClass instance at 0x0232B7B0>
    >>> m = MyClass()
    >>> w() is None
    False
    >>> w
    <weakref at 02329660; dead>
    >>> w()
    >>> w() is None
    True
    Si w() is None est False, c'est que l'objet w() n'est pas None, donc il existe, non ? Et si w() n'est pas None, c'est que la référence w existe aussi.


    Quand tu écris
    « j'avais déjà perdu la référence à l'objet référencé »
    c'est une affirmation relative à ce que fait le programme, ou c'est une conclusion tirée d'une ligne affichée ?
    Qu'est ce que ça veut dire "reférence perdue" ?

    Même interrogation sur
    « Pourtant, le garbage collector ne l'avait visiblement pas encore détruit. » ?


    Peux-tu développer un peu stp ?
    .

  9. #9
    Membre émérite
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Par défaut
    w, dans ce code, est une weakref. Les weakref renvoient, lorsqu'on les invoque, soit la référence vers l'objet qu'ils "contiennent", soit None si cet objet est détruit.

    Citation Envoyé par eyquem Voir le message
    « j'avais déjà perdu la référence à l'objet référencé »
    Oui, le terme de "référence perdue" n'est peut-être pas des plus clairs...
    Dans mon code, j'assigne une première fois m à une instance de MyClass, par l'instruction m = MyClass().
    Un peu plus loin, je réassigne m à une autre instance, par la même instruction m = MyClass(). Dans mon cas, la référence à la première instance est donc perdue, puisque je n'ai plus aucun lien pour la retrouver (hormis la weakref, mais qui ne compte donc pas).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    m = MyClass()
    ...
    m = MyClass() # Je n'ai plus aucun lien vers la première instance
    C'était donc une affirmation relative à ce que fait le programme, comme tu le dis si bien.

    « Pourtant, le garbage collector ne l'avait visiblement pas encore détruit. » ?
    Tu n'ignores pas je crois que le garbage collector se charge de détruire les objets qui ne sont plus nécessaires pour libérer de la mémoire.
    Or, un objet est considéré comme n'étant "plus nécessaire" dès le moment où l'on n'a plus de référence vers ledit objet.
    Dans mon cas, après la deuxième instantiation de MyClass, plus aucune référence "forte" (par opposition aux weakref) n'existait vers la première instance.
    Cette première instance peut dès lors être considérée comme n'étant "plus nécessaire": elle est à la disposition du garbage collector.

    Le fait que le w() is None résultait en False dénote le fait que cette instance existait toujours malgré qu'il n'existe plus aucune référence "forte" vers elle.

    Il nous manque une information, toutefois, pour arriver à une quelconque conclusion:
    Pour que le gc puisse savoir quels objets ne sont plus nécessaires, il doit avoir un compte de référence. (NB: c'est le cas pour Python, il peut en être autrement pour d'autres langages. Java, par exemple, procède différemment.)
    Pour compter ces références, l'API C de Python offre deux macros:
    Py_INCREF (increase ref count: incrémenter le compte de références)
    Py_DECREF (decrease ref count: décrémenter le compte de références)

    On pourrait alors croire que lorsqu'on effectue un Py_DECREF sur un objet, le gc vérifie immédiatement combien de références il reste. Si c'est zero, alors l'objet est détruit immédiatement.

    Or, on a bien vu ici que ce n'était pas le cas: il y a eu un long délai entre la perte de référence et la réelle délétion de l'objet.
    On peut donc en déduire que le gc de notre ami Python s'exécute périodiquement, indépendamment des instructions.

  10. #10
    Membre chevronné
    Profil pro
    Ingénieur sécurité
    Inscrit en
    Février 2007
    Messages
    574
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Février 2007
    Messages : 574
    Par défaut
    Citation Envoyé par Antoine_935 Voir le message
    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
    >>> class Collector(list):
    ...     def __call__(self, init):
    ...         def newInit(itself, *args, **kwargs):
    ...             init(itself, *args, **kwargs)
    ...             self.append(itself)
    ...
    ...         return newInit
    ...
    >>> collector = Collector()
    >>>
    >>> class MyTrackedClass(object):
    ...     @collector
    ...     def __init__(self):
    ...         pass
    ...
    >>> MyTrackedClass()
    <__main__.MyTrackedClass object at 0x0231E550>
    >>> MyTrackedClass()
    <__main__.MyTrackedClass object at 0x0231E430>
    >>> MyTrackedClass()
    <__main__.MyTrackedClass object at 0x0231E710>
    >>> MyTrackedClass()
    <__main__.MyTrackedClass object at 0x0231E730>
    >>> collector
    [<__main__.MyTrackedClass object at 0x0231E550>, <__main__.MyTrackedClass object
     at 0x0231E430>, <__main__.MyTrackedClass object at 0x0231E710>, <__main__.MyTra
    ckedClass object at 0x0231E730>]
    Belle utilisation des décorateurs, j'aime beaucoup... . J'avais jamais trouvé de réel cas d'utilisation, mis à part pour les framework...

  11. #11
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 15
    Par défaut
    J'ai une question en rapport avec ça : une fois que l'on a mis nos objets dans une liste pour les enregistrer, le but est quand même de pouvoir retrouver l'objet que l'on a rangé.

    En supposant que l'on veuille sélectionner (retourner) l'objet en le sélectionnant par un paramètre (par exemple, tous les objets créés ont un attribut nommé name='...'). Est-il possible de faire la recherche d'une manière élégante dans une liste ?

    Il est certes possible de faire une boucle du type :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    L = ['objet_1' , 'objet_2' , 'objet_3']
     
    for i in L:
        if i.name == 'ce que je cherche':
            i.operation()
            return
        else:
            return None
    Mais est-il possible d'obtenir ce même résultat en une ligne, pour l'intégrer de manière plus élégante à une instruction (si je souhaite par exemple appliquer ensuite la méthode operation() au i retourné) et ceci, notamment dans les cas où on veut appliquer plusieurs fois une méthode à des objets que l'on veut aller chercher dans cette liste ?

    Pour chercher dans une liste contenant des string, il serait par exemple possible de taper :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    L[L.index('la chaîne recherchée')].capitalize()
    au lieu de

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    for i in L:
        if i == 'la chaîne recherchée':
            i.capitalize()
            return
        else:
            return
    Il faudrait pouvoir faire un .index('...') sur une liste contenant des objets, sauf que les objets seraient vus comme leur attributs objets.name et non des objets simples ...

  12. #12
    Membre émérite
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Par défaut
    Citation Envoyé par Xoxocs Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    L[L.index('la chaîne recherchée')].capitalize()
    Ton exemple est peut-être mal choisi, puisqu'il ne modifie pas l'objet qui est dans la liste: les chaines de caractères sont immuables.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    >>> a = "hello"
    >>> a.capitalize()
    'Hello'
    >>> a
    'hello'
    Il est possible d'intégrer assez élégamment la recherche d'objets selon leurs propriétés, en ajoutant une petite fonctionnalité au Collector.
    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 Collector(list):
    ...     def __call__(self, init):
    ...         def newInit(itself, *args, **kwargs):
    ...             init(itself, *args, **kwargs)
    ...             self.append(itself)
    ...
    ...         return newInit
    ...
    ...     def query(self, **params):
    ...         result = []
    ...
    ...         for item in self:
    ...             if Collector.match(item, params):
    ...                 result.append(item)
    ...
    ...         return result
    ...
    ...     @staticmethod
    ...     def match(object, params):
    ...         for attr in params:
    ...             try:
    ...                 if getattr(object, attr) != params[attr]:
    ...                     return False
    ...
    ...             except AttributeError:
    ...                 return False
    ...
    ...         return True
    ...
    >>> collector = Collector()
     
    >>> class MyClass(object):
    ...     @collector
    ...     def __init__(self, name, value):
    ...         self.__name = name
    ...         self.__value = value
    ...
    ...     @property
    ...     def name(self):
    ...         return self.__name
    ...
    ...     @property
    ...     def value(self):
    ...         return self.__value
    ...
     
    >>> MyClass("Hello", 25)
    <__main__.MyClass object at 0x0232C7F0>
    >>> MyClass("Haha", 25)
    <__main__.MyClass object at 0x0232CB90>
    >>> MyClass("Hello", 32)
    <__main__.MyClass object at 0x0232CCB0>
     
     
    >>> collector.query(name="Hello")
    [<__main__.MyClass object at 0x0232C7F0>, <__main__.MyClass object at 0x0232CCB0
    >]
    >>> collector.query(name="Hello", value=25)
    [<__main__.MyClass object at 0x0232C7F0>]
    >>> collector.query(value=25)
    [<__main__.MyClass object at 0x0232C7F0>, <__main__.MyClass object at 0x0232CB90
    >]
    Après, tu peux implémenter des fonctionnalités plus exotiques, comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    # Rechercher les objets dont "value" est plus grand que 24
    collector.query(value__greater=24)

  13. #13
    Membre émérite
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Par défaut
    Je me permets de faire un double post, puisqu'il s'agit de deux choses très différentes.

    Citation Envoyé par dahtah Voir le message
    Belle utilisation des décorateurs, j'aime beaucoup... . J'avais jamais trouvé de réel cas d'utilisation, mis à part pour les framework...
    Merci La première fois que j'ai fait ce genre de chose, c'était dans le cadre de ma librairie Aspyct (qui est un peu abandonnée pour l'instant d'ailleurs).

    Les décorateurs ont de très nombreux usages, mais la plupart tournent autour de l'orienté aspect (collection d'objets, logging, injection de dépendances...). Si ça t'intéresse, jette un oeuil à cette lib dont je parle plus haut, tu devrais en apprendre pas mal sur l'usage des décorateur

  14. #14
    Membre averti
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 15
    Par défaut
    Citation Envoyé par Antoine_935 Voir le message
    Ton exemple est peut-être mal choisi, puisqu'il ne modifie pas l'objet qui est dans la liste: les chaines de caractères sont immuables.
    En effet, je reconnais que ce n'était pas le meilleur exemple que je puisse trouver

    Merci pour ta réponse en tout cas ^^

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

Discussions similaires

  1. Bonnes pratiques de protections individuelles
    Par Community Management dans le forum Sécurité
    Réponses: 23
    Dernier message: 11/06/2024, 11h23
  2. [2012] Bonne Pratique de déploiement SSISDB en multi-instance
    Par GeekMokona dans le forum SSIS
    Réponses: 2
    Dernier message: 08/12/2014, 11h52
  3. Ouvrage de référence sur les bonnes pratiques pour le design d'un site
    Par Schim59 dans le forum Webdesign & Ergonomie
    Réponses: 0
    Dernier message: 24/07/2012, 08h43
  4. Réponses: 0
    Dernier message: 02/12/2010, 15h39
  5. Bonne pratique : Résolution de portée ou référence ?
    Par FrontLine dans le forum Langage
    Réponses: 20
    Dernier message: 20/05/2008, 09h31

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