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 :

Envoyer une méthode en argument d'un décorateur


Sujet :

Python

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Technicien SIG
    Inscrit en
    Novembre 2012
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations professionnelles :
    Activité : Technicien SIG
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3
    Par défaut Envoyer une méthode en argument d'un décorateur
    Bonjour

    Je travaille actuellement sur un programme Python utilisant des décorateurs.
    L'un de ces décorateurs me permet d'effectuer un "try" sur une méthode, afin d'agir en cas d'erreur (générée par mes soins) :

    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
     
    def dec_erreur(fonction_erreur):
        def decorateur(fonction_in):
            def fonction_out(*args, **kwargs):
                try:
                    # Essai la fonction en entrée
                    result = fonction_in(*args, **kwargs)
                except AbortError:
                    # Appelle la fonction agissant en cas d'erreur
                    return fonction_erreur()
                else:
                    # Envoi du résultat de la fonction initiale
                    return result
            return fonction_out
        return decorateur
    Ce décorateur est placé avant la méthode d'une classe :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    class MaClasse:
        @dec_erreur(fonction_erreur)
        def maMethode(self):
            ...
    Le faite de créer ce décorateur me permet de réduire le code au maximum. Je n'ai qu'à spécifier la fonction à effectuer en cas d'erreur, ce qui peut me permettre d'effectuer certaines choses en cas de plantage.

    Placer une fonction sans argument en paramètre du décorateur n'est pas compliqué. Mais j'aimerais pouvoir appeler une méthode en cas d'erreur, ce qui me permettrait d'accéder à toutes mes variables utiles (connexions à des DB, objets...).

    Comment pouvoir envoyer en paramètre du décorateur une référence à la méthode d'une classe?

    Merci

  2. #2
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 695
    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 695
    Par défaut
    Salut,

    Je ne comprends pas trop la construction que vous cherchez à faire.
    Ecrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class MaClasse:
        @dec_erreur(fonction_erreur)
        def maMethode(self):
            ...
    équivaut à:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    class MaClasse:
        def maMethode(self):
            ...
        maMethode = dec_erreur(fonction_erreur)(maMethode)
    A partir de là, si m1 est une méthode de MaClasse à passer dans dec_erreur,
    pa de problème pour construire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
        maMethode = dec_erreur(fonction_erreur, m1)(maMethode)
        maMethode = dec_erreur(fonction_erreur)(maMethode, m1)
    Dans tous les cas, les m1 et maMethode récupérés dans dec_erreur seront "unbound" => çà fonctionne parce qu'on "binde" à la main la fonction à self qui sera le premier des arguments passés dans *args.
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #3
    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,

    Le problème me semblait facile à résoudre, mais ce n'est pas le cas.

    Faire appeler une méthode par un décorateur nécessite pour celui-ci de trouver l'adresse de l'instance (=self), mais celle-ci est inconnue au moment de l'initialisation du décorateur, puisqu'il n'y a pas encore eu d'instanciation de la classe.

    La solution que j'ai trouvée est celle-ci:

    1- la méthode à appeler en cas d'erreur est transmise en argument au décorateur sous forme de str.

    2- lors de l'appel de la méthode décorée, on retrouve le self comme 1er argument (=> args[0]). On peut alors retrouver la méthode à appeler en cas d'erreur grâce à getattr(args[0], "fonction_erreur").

    Exemple (Python 2.7):

    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
    import functools
     
    ############################################################################# 
    def dec_erreur(fonction_erreur=""):
        """ decorateur avec passage d'arguments """
        def _decorateur(fonc):
            @functools.wraps(fonc)
            def appelfonc(*args, **kwargs):
                try:
                    result = fonc(*args, **kwargs)
                except Exception:
                    result = None
                    if hasattr(args[0], fonction_erreur):
                        result = getattr(args[0], fonction_erreur)()
                return result    
            return appelfonc
        return _decorateur
     
    ############################################################################# 
    class MaClasse(object):
     
        @dec_erreur("fonction_erreur")
        def maMethode(self):
            x = 5/0
     
        def fonction_erreur(self):
            """méthode appelée par le décorateur 'dec_erreur' en cas d'erreur"""
            print u"erreur!"
     
    ############################################################################# 
    if __name__ == "__main__":
        x = MaClasse()
        x.maMethode()
    Ici, l'appel à maMethode déclenche l'exception (à cause de la division par zéro) qui appelle la méthode fonction_erreur, qui affiche "erreur!"

  4. #4
    Futur Membre du Club
    Homme Profil pro
    Technicien SIG
    Inscrit en
    Novembre 2012
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations professionnelles :
    Activité : Technicien SIG
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3
    Par défaut
    Merci tyrtamos

    Cela répond parfaitement à ce que je voulais faire. J'ai juste poussé un peu la réflexion pour que le décorateur puisse appeler une méthode, une fonction ou la méthode d'une autre classe.

  5. #5
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 695
    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 695
    Par défaut
    Citation Envoyé par taxodium Voir le message
    Cela répond parfaitement à ce que je voulais faire. J'ai juste poussé un peu la réflexion pour que le décorateur puisse appeler une méthode, une fonction ou la méthode d'une autre classe.
    Python autorisant l'héritage multiple, les mixins méritent d'être regardés:

    On part de la classe originale:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class A:
     
        def m1(self, *a, **k):
            print ('A.m1')
            raise Exception()
     
        def m2(self):
            print ('A.error')
    Puis on crée un class M qui tricotera les méthodes de A:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class M:
     
        def m1(self, *a, **k):
            print ('M.m1')
            try:
               return super().m1(*a, **k)
            except:
               return self.error()
     
        def error(self):
            raise Exception('not implemented')
    Puis on colle les deux:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    class C(M, A): 
        error = A.m2
    L'héritage multiple permet de respecter le chaînage apporté par le décorateur (appel du décorateur puis appel de la fonction décorée) sans passer par des fonctions emboîtées.
    Et dans ce cas précis c'est quand même plus lisible, enfin je crois.
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  6. #6
    Membre Expert
    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
    Par défaut
    La méthode de wiztricks est effectivement plus claire, mais elle nécessite de créer une nouvelle classe mixin pour chaque méthode que l'on désire instrumenter de la sorte...

    Le méthode de tyrtamos peut fonctionner sans devoir passer par une string, du moment qu'on déclare la méthode qui traite l'erreur avant de l'utiliser:
    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
    import functools
     
    ############################################################################# 
    def dec_erreur(fonction_erreur):
        """ decorateur avec passage d'arguments """
        def _decorateur(fonc):
            @functools.wraps(fonc)
            def appelfonc(self, *args, **kwargs):
                try:
                    result = fonc(self, *args, **kwargs)
                except Exception:
                    result = fonction_erreur(self)
                return result    
            return appelfonc
        return _decorateur
     
    ############################################################################# 
    class MaClasse(object):
     
        def fonction_erreur(self):
            """méthode appelée par le décorateur 'dec_erreur' en cas d'erreur"""
            print u"erreur!"
     
        @dec_erreur(fonction_erreur)
        def maMethode(self):
            x = 5/0
     
    ############################################################################# 
    if __name__ == "__main__":
        x = MaClasse()
        x.maMethode()

  7. #7
    Futur Membre du Club
    Homme Profil pro
    Technicien SIG
    Inscrit en
    Novembre 2012
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations professionnelles :
    Activité : Technicien SIG
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Novembre 2012
    Messages : 3
    Par défaut
    Merci à tous pour votre aide, mais je m'en tient à le méthode de tyrtamos pour le moment.
    Dans mon service, nous devons réaliser un grand nombre d'applications en peu de temps, c'est ce qui nous a poussé à créer une bibliothèque de fonctions et classes que nous utilisons régulièrement. Ainsi, pour chaque chose réalisée, nous devons essayé de généraliser au maximum afin de pouvoir l'intégrer à notre bibliothèque.
    Le but est de pouvoir réutiliser ces fonctionnalités de façon simple et rapide dans nos futures applications. Voilà pourquoi elles doivent être le plus souple possible.

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

Discussions similaires

  1. Réponses: 6
    Dernier message: 09/02/2009, 13h28
  2. [POO] passer une méthode en argument
    Par gorgonite dans le forum Langage
    Réponses: 4
    Dernier message: 09/11/2007, 18h56
  3. Du javascript en argument d'une méthode java
    Par mooosh dans le forum Général JavaScript
    Réponses: 3
    Dernier message: 27/07/2007, 13h33
  4. [C#] Passage d'une méthode en argument
    Par Husqvarna dans le forum C#
    Réponses: 8
    Dernier message: 15/11/2006, 12h54
  5. Réponses: 1
    Dernier message: 10/10/2006, 15h14

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