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 :

variable de classe, d'instance et __get__


Sujet :

Python

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    23
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 23
    Points : 16
    Points
    16
    Par défaut variable de classe, d'instance et __get__
    bonjour,

    ci dessous un petit bout de code.
    je ne comprends pas pourquoi j'arrive à déclenché le descripteur __get__ en variable de classe et pas en variable d'instance

    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
     
    class Prop(object):
     
        def __get__(self, instace,  owner):
            return  "Prop __get__"
     
     
    class Model1(object):
        a=Prop()
     
    class Model2(object):
       def __init__(self):
           self.a = Prop()
     
     
    testModel1 = Model1()
    testModel2 = Model2()
     
    print testModel1.a
    print testModel2.a
     
    #>>> Prop __get__
    #>>><__main__.Prop object at 0x0000000002718EF0>
    pourtant, d'apres la doc :
    The starting point for descriptor invocation is a binding, a.x. How the arguments are assembled depends on a:

    Direct Call
    The simplest and least common call is when user code directly invokes a descriptor method: x.__get__(a).

    Instance Binding
    If binding to a new-style object instance, a.x is transformed into the call: type(a).__dict__['x'].__get__(a, type(a)).

    Class Binding
    If binding to a new-style class, A.x is transformed into the call: A.__dict__['x'].__get__(None, A).
    il me semble que je suis dans le cas de l'instance binding, mais j'avoue que je peux avoir des ( grosses ) lacunes en anglais.

    Merci pour votre aide

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 424
    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 424
    Points : 37 002
    Points
    37 002
    Par défaut
    Salut,

    L'idée des descripteurs est d'insérer un étage d'indirection lors de l'accès à la valeur de l'attribut d'une instance. Si vous posez l'indirection "après", çà ne fait rien du tout.
    Courte l'explication? Relisez la encore.
    Tant de choses ont déjà été écrites sur le sujet.
    Elles ne prennent leur sens que si vous mettez la main à la patte ;-)

    - W

  3. #3
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Points : 1 384
    Points
    1 384
    Par défaut
    Bonsoir,

    Je ne pense pas que les descripteurs puissent être utilisés de cette façon.
    Dans la doc:
    The following methods only apply when an instance of the class containing the method (a so-called descriptor class) appears in the class dictionary of another new-style class, known as the owner class.
    Dans le cas de Model2, l'instance du descripteur n’apparaît pas dans le dictionnaire de la classe mais dans le dictionnaire de l'instance.

    Pour ce qui est de instance binding et class binding:
    testModel1.a est une instance binding
    Model1.a est une class binding

  4. #4
    Membre à l'essai
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    23
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 23
    Points : 16
    Points
    16
    Par défaut
    Bonjour,

    Je pense que effectivement, j'ai plus un problème de conception que d''implémentation.

    Je vais revoir ma copie.

    Merci pour vos lumières

    K.

  5. #5
    Membre habitué
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    103
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 103
    Points : 135
    Points
    135
    Par défaut
    Si un exemple d'usage de __get__ peut aider:

    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
     
    class decorator_without_get(object):
        def __init__(self, func):
            self._func = func
     
        def __call__(self, *args, **kwargs):
            return self._func(*args, **kwargs)
     
    class decorator_with_get(decorator_without_get):
        def __get__(self, instance, owner):
            return functools.partial(self.__call__, instance)
     
     
    class A(object):
       @decorator_without_get
       def dummy_without_get(self):
           pass
     
       @decorator_with_get
       def dummy_with_get(self):
           pass
     
    a = A()
     
    a.dummy_without_get()  
    # KO: TypeError: dummy_without_get() takes exactly 1 argument (0 given)
    # on appelle decorator_without_get.__call__(self_decorator)
    # donc dummy_without_get sans arguments
     
    a.dummy_with_get()     
    # OK: on appelle decorator_with_get.__get__(a)(self_decorator) 
    # donc decorator_with_get.__call__(self_decorator, a)
    # donc dummy_with_get avec a comme argument comme il faut !
    PS: si qqun peut valider mon explication de pourquoi les 2 fonctions décorées fonctionnent ou pas

  6. #6
    Membre expérimenté
    Homme Profil pro
    Inscrit en
    Mars 2007
    Messages
    941
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2007
    Messages : 941
    Points : 1 384
    Points
    1 384
    Par défaut
    Citation Envoyé par ZZelle Voir le message
    si qqun peut valider mon explication de pourquoi les 2 fonctions décorées fonctionnent ou pas
    Je suis d'accord avec l'explication, après avoir compris que self_decorator fait référence à A.dummy_without_get dans le premier cas et à A.dummy_with_get dans le second.

    En détaillant un peu plus:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    a.dummy_without_get()
    --> A.dummy_without_get()
    --> A.dummy_without_get.__call__()
    --> decorator_without_get.__call__(A.dummy_without_get)
    et non pas
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    a.dummy_without_get()
    --> A.dummy_without_get(a)
    --> A.dummy_without_get.__call__(a)
    --> decorator_without_get.__call__(A.dummy_without_get, a)
    comme on pourrait s'y attendre.

    Avec __get__ cela donne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    a.dummy_with_get()
    --> A.dummy_with_get()  # a est mémorisé; ce modèle par substitutions a ses limites...
    --> decorator_with_get.__get__(A.dummy_with_get, a, A)()
    --> partial(A.dummy_with_get.__call__, a)()
    --> A.dummy_with_get.__call__(a)
    --> decorator_with_get.__call__(A.dummy_with_get, a)
    Pour rendre les choses un peu plus claires, j'aurais écris la méthode __call__ comme ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
        def __call__(self, instance, *args, **kwargs):
            return self._func(instance ,*args, **kwargs)
    Mais je me pose la question de pourquoi est-ce ainsi ?

    Tentative d'explication: lors de l'appel du décorateur, la fonction décorée n'est encore réellement qu'une fonction, créée dans le contexte de la définition de la classe A. On peut le vérifier en affichant le type de A.dummy_without_get._func. Lorsque la définition des attributs de A est terminée, l'objet "classe A" est créé, et les fonctions qui sont contenues dans son dictionnaire sont transformées en méthodes. Lorsqu'un décorateur retourne une fonction, tout se passe bien, mais ici le décorateur retourne une instance d'un objet qui n'est pas une fonction, et n'est donc pas transformé en méthode. Le passage de l'instance comme premier argument ne se fait donc pas automatiquement comme pour un appel de méthode.

  7. #7
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 424
    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 424
    Points : 37 002
    Points
    37 002
    Par défaut
    Salut,

    C'est ainsi parce que le décorateur "class" ne devrait pas être utilisé ainsi.
    Si on prend le décorateur "fonction", basique:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    def decorate(f):
        def wrapper(*args, **kwargs):
            f(*args, **kwargs)
        return wrapper
    Pour décorer:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    class A:
        @decorate
        def dummy(self): print('dummy')
    est équivalent à:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    class A:
        def dummy(self): print('dummy')
    A.dummy = decorate(A.dummy)
    Dans les deux cas, on a emballé dummy dans "wrapper" et l'appelant sera content.

    Si on transforme decorate en "class":
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class D(object):
        def __init__(self, debug=False):
             self._debug=debug
        def __call__(self, f):
            def wrapper(*args, **kwds):
                f(*args, **kwds)
            return wrapper
    Mais dans ce cas:
    A.dummy = decorate(A.dummy) est incorrect.
    Il faudrait écrire:
    A.dummy = decorate()(A.dummy)
    pour que A.dummy contienne le "wrapper" qui remplace f (et non None).

    Sur qu'on peut faire tomber cela en marche!
    1. sauvegarder la fonction à l'__init__ plutôt que la récupérer au call
    2. écrire un __get__ pour "binder" la méthode qui est devenue "None"
    Mais on se retrouve à écrire du code pour compenser une utilisation 'border line'.

    Si on veut avoir un état associé au décorateur, on peut aussi empiler les fonctions:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    def decorate(debug=False):
        def inner_decorator(f):
            def wrapper(*args, **kwargs):
                f(*args, **kwargs)
            return wraps(f)(wrapper)
        return inner_decorator
    - W

  8. #8
    Membre habitué
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    103
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 103
    Points : 135
    Points
    135
    Par défaut
    Citation Envoyé par dividee Voir le message
    Je suis d'accord avec l'explication, après avoir compris que self_decorator fait référence à A.dummy_without_get dans le premier cas et à A.dummy_with_get dans le second.
    En effet


    C'est pas le sujet du post mais tt de même ...
    Citation Envoyé par wiztricks Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class D(object):
        def __init__(self, debug=False):
             self._debug=debug
        def __call__(self, f):
            def wrapper(*args, **kwds):
                f(*args, **kwds)
            return wrapper
    Je vois pas très bien l'intérêt de s’embarrasser avec une classe si c'est pour faire au final coller le décorateur "func" dans le __call__

    Pour moi l'avantage de cette dite bidouille est que la classe suivante donne une classe de base pour faire des décorateurs (pouvant décorer des fonctions et des méthodes). C'est d'autant plus utile dans une équipe où tout le monde ne partage pas la même connaissance de python (en particulier sur l'utilisation de functools, des fonctions imbriquées, hein les javaistes !).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class decorator(object):
        def __init__(self, func):
            self._func=func
            functools.update_wrapper(self, func)
     
        def __call__(self, *args, **kwargs):
            # simple dummy decorator
            return self._func(*args, **kwargs) 
     
        def __get__(self, instance, owner):
            funcpart = functools.partial(self.__call__, instance)
            functools.update_wrapper(funcpart, self)
            return funcpart

  9. #9
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 424
    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 424
    Points : 37 002
    Points
    37 002
    Par défaut
    Salut,

    Citation Envoyé par ZZelle Voir le message
    Pour moi l'avantage de cette dite bidouille est que la classe suivante donne une classe de base pour faire des décorateurs (pouvant décorer des fonctions et des méthodes).
    Fonctionnellement functools.partial fait la même chose que ce qui est construit avec les fonctions imbriquées. C'est juste le style d'écriture qui change mais c'est plus une question de goûts, d'habitudes,... qui se respectent.

    Si vous débutez en Python, autant prendre le temps d'apprécier les différentes formes d'expression et d'utiliser celle qui sera la plus appropriée au cas d'utilisation donné.

    Cordialement,
    - W

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

Discussions similaires

  1. Variable d'instance et variable de classe
    Par glycerine dans le forum C++
    Réponses: 6
    Dernier message: 02/03/2015, 14h37
  2. Variables de classe vs Variables d'instance
    Par webja dans le forum Langage
    Réponses: 2
    Dernier message: 15/03/2007, 10h28
  3. Variable de classe JPanel
    Par Janitrix dans le forum AWT/Swing
    Réponses: 4
    Dernier message: 11/12/2005, 18h50
  4. débutant:utilisation de variable entre classe
    Par troojan dans le forum MFC
    Réponses: 1
    Dernier message: 08/12/2005, 00h31
  5. Réponses: 6
    Dernier message: 23/09/2005, 13h54

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