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 :

Decorateur et registre


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 Decorateur et registre
    Bonjour à tous,

    j'essaye d'exploiter les @decorateur de python pour inscrire dans un registre ( un dict tout simplement ) une fonction.
    Pour me simplifier la tache, décorateur et fonction ont des paramètres.

    si dessous un code qui ne fonctionne pas

    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
     
    import functools
     
    # mon registre
    registry_model = {}
     
    # ma class fake
    class Klass(object):pass
     
    # dans mon implémentation, j'utilise les metaclass pour enregistrer mon model
    registry_model["Klass"] = {}
     
     
    def register_io_write(model=None,  _type="xml"):
        " Register io write function for the model in _type context"
     
        def decorated(func):
            @functools.wraps(func)
            def wrapper(*args, **kwargs):
                registry_model[model.__name__].update({("io","write", _type):func})
                return func(*args, **kwargs)
            return wrapper
        def decorator(func):
            return decorated(func)
        return decorator
     
    @register_io_write(Klass, "xml")
    def write_model(*args, **kwargs):
        print args, kwargs
     
     
    print registry_model
    print "Done"

    en faite, je ne passe jamais dans la fonction wrapper
    et si je sort de cette fonction, je ne connait plus la fonction decorée

    peut être n'est ce pas possible ? ou sinon je me perd dans mon raisonnement avec les décorateurs

    une idée à me soumettre, une piste à suivre?

    merci pour vos lumières

    Kermit

  2. #2
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    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 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Petite correction du décorateur:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    def register_io_write(model=None,  _type="xml"):
        " Register io write function for the model in _type context"
        def decorated(func):
            @functools.wraps(func)
            def wrapper(*args, **kwargs):
                registry_model[model.__name__].update({("io","write", _type):func})
                return func(*args, **kwargs)
            return wrapper
        return decorated # decorator
    Et pour que le décorateur fasse son boulot, il faut appeler la fonction décorée:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    write_model(1, 2, 3, a=4, b=5, c=6)
    print registry_model
    print "Done"
    résultats:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    (1, 2, 3) {'a': 4, 'c': 6, 'b': 5}
    {'Klass': {('io', 'write', 'xml'): <function write_model at 0x02973C70>}}
    Done
    ça a l'air de marcher?
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

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

    C'est un sujet "compliqué", voir la discussion

    Votre code marcherait mieux écrit ainsi:
    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 register_io_write(model=None,  type_="xml"):
        def decorated(func):
            registry_model[model.__name__].update({("io","write", type_):func})
            def wrapper(*args, **kwargs):
                return func(*args, **kwargs)
            return functools.wraps(func)(wrapper)
        return decorated
     
    @register_io_write(Klass, "xml")
    def write_model(*args, **kwargs):
        print args, kwargs
     
    print registry_model
    print write_model(1,2,3)
    print "Done"
    Après, pour essayer d'expliquer un peu, écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    @register_io_write(Klass, "xml")
    def write_model(*args, **kwargs):
         ...
    équivalaut à écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    write_model = register_io_write(Klass, "xml")(write_model)
    <=> remplacer la définition de la fonction
    ce qui va:
    1. exécuter la première fonction imbriquée (decorated) avec write_model en paramètre
    2. et retourner une instance de "wrapper" avec son contexte "autour".
    => s'il faut faire des trucs à l'enregistrement de la fonction, c'est dans le corps de la fonction imbriquée, le "wrapper" n'étant appelé qu'à la place de la fonction wrappée, je suppose que c'est trop tard...

    Une autre façon de dérouler cela est de passer par des classes:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class register_io_write(object):
        def __init__(model=None,  type_="xml"):
              self.model = model
              self.type_ = type_
        def __call__(self, func):
            registry_model[self.model.__name__].update(
                          {("io","write", self.type_):func})
     
            def wrapper(*args, **kwargs):
                return func(*args, **kwargs)
            return functools.wraps(func)(wrapper)
    comme c'est plus verbeux, on voit mieux ce qu'il se passe (enfin peut être).

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

  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,

    merci pour vos réponses.

    Effectivement, mon inscription:

    registry_model[model.__name__].update({("io","write", _type):func})
    n' était pas au bonne endroit.

    Merci pour les explications car je n'avais pas compris toutes les subtilités ( surtout avec la recombinaison du decorateur @functools.wraps(func) )

    @wiztricks
    j'aime beaucoup la version avec la class, plus clair de mon point de vue.

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

    c'est un détail, mais créer un wrapper qui ne fait qu'appeler la fonction d'origine avec les mêmes arguments, ça n'a pas vraiment d'intérêt.

    Plutôt que:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
            def wrapper(*args, **kwargs):
                return func(*args, **kwargs)
            return functools.wraps(func)(wrapper)
    Autant écrire:

  6. #6
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    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 287
    Points : 36 776
    Points
    36 776
    Par défaut
    c'est un détail, mais créer un wrapper qui ne fait qu'appeler la fonction d'origine avec les mêmes arguments, ça n'a pas vraiment d'intérêt.
    Si on se limite à la fonction "registry", cela est très juste. Mais dans ce cas, on pourrait même se débarrasser des decorators!
    Pour réaliser la mise en registry de la fonction, il suffit d'écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    def write_model(...):
        ...
    register_io_write(Klass, "xml")(write_model)
    Supporter une mise en forme:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    @register_io_write(Klass, "xml")
    def write_model(...):
        ....
    coûte au moins toutes les lignes de codes du décorator et la compréhension de ce qui se passe pour le faire tomber en marche.
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  7. #7
    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
    mon intention était de me faciliter la vie pour l'enregistrement de module type "plugins"

    en oubliant un peu cette histoire de décorateur, pour enregistrer de manière "automatique" un nouveau module, il vaut mieux utiliser les metaclass ?? avec une class callable ??

    je sais, on dérive sur le sujet

    il y a également un autre exemple dans 'Dive into Python', qui utilise global().
    Mais je ne mis suis pas penché, car j'ai toujours entendu dire que, pour la programmation en général, les "global, c'est mal"
    De plus, l'exemple exploite le nom de la fonction (FileInfo, MP3FileInfo, etc..) et je trouve cela dangereux

    des fous furieux du C++ a coté de moi me dise que explicite et mieux que implicite, mais on perd un peu le charme de python et de son introspection.

    Votre avis m’intéresse vivement, car c'est la première fois que j'attaque un développement "lourd" en python, qui de sur plus, pour le fun, aura des parties optimiser en C++

    Merci

    Kermit

  8. #8
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    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 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Salut,
    en oubliant un peu cette histoire de décorateur, pour enregistrer de manière "automatique" un nouveau module, il vaut mieux utiliser les metaclass ?? avec une class callable ??
    Désolé, je ne comprend pas la question.
    module, class et fonction sont des objets de nature complètement différente.

    Un module est, par défaut, associé à un fichier et un fichier est dans un file system. Class et metaclass (python) font partie d'un univers différent.
    => Il faudra construire avec d'autres outils!

    j'ai toujours entendu dire que, pour la programmation en général, les "global, c'est mal"
    En Python globals() est le nom donné au dictionnaire associé au module/script "courant". Chaque module ayant son globals(), ils ne sont pas si "globaux". En fait le seul globals() global est celui du module builtins.

    Un module est par défaut singleton.
    Utiliser ce globals()/dictionnaire là plutôt que l'instance d'un autre dict promu "singleton", quelle importance? La forme ou la fonction?

    De plus, l'exemple exploite le nom de la fonction (FileInfo, MP3FileInfo, etc..) et je trouve cela dangereux
    "register_io_write(Klass, "xml")(write_model)" s'interprête:
    récupérer l'objet qui correspond à l'entrée "write_model" de globals() et...
    Le nom de l'objet fonction est un objet de type str accessible via .__name__.

    des fous furieux du C++ a coté de moi me dise que explicite et mieux que implicite, mais on perd un peu le charme de python et de son introspection.
    Le compilateur les a souvent rappelé à l'ordre lorsqu'ils ont osé oublié ce sage principe. Python étant interprêté et basé sur du "duck typing", il est clair que le programmeur d'un langage compilé mettra du temps à trouver ses repères.

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

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

Discussions similaires

  1. [Windows]accès base de registre windows
    Par Greg01 dans le forum API standards et tierces
    Réponses: 27
    Dernier message: 05/06/2007, 15h14
  2. Accès à la base de registre windows à distance
    Par xavame dans le forum Sécurité
    Réponses: 4
    Dernier message: 13/07/2005, 15h23
  3. Comparaison d'un registre 8 bits avec une variable 32 bits
    Par tupperware dans le forum x86 32-bits / 64-bits
    Réponses: 3
    Dernier message: 15/10/2002, 10h25
  4. registre de connexion windows internet
    Par lafaryan dans le forum Web & réseau
    Réponses: 2
    Dernier message: 21/08/2002, 12h52
  5. Utilisez vous la base de registres ?
    Par gRRosminet dans le forum C++Builder
    Réponses: 8
    Dernier message: 04/06/2002, 13h55

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