Partage
  • Partager sur Facebook
  • Partager sur Twitter

Aide pour les décorateurs

Sujet résolu
Anonyme
    20 septembre 2014 à 14:51:23

    Salut, 

    Je code en ce moment un décorateur qui permet de forcer les arguments de la fonction "wrappée" à être d'un certain type:

    def force(**kwargs):
    	types = kwargs
    	def decorator(func):
    		def wrapper(**innerkwargs):
    			for k, v in innerkwargs.items():
    				if not isinstance(v, types[k]):
    					raise TypeError("{} should be a {} object (got {}).".format(k, types[k].__name__, v.__class__.__name__))
    			print("start wrapped func...")
    			return func(**innerkwargs)
    		return wrapper
    	return decorator
    
    @force(a=str, b=int)
    def put(a='*', b=6): print(a*b)

    Ça marche bien mais uniquement lorsque j'appelle la fonction avec des arguments à mot clés et pas des arguments positionnels:

    >>> put(a='*', b=42)
    start wrapped func...
    ******************************************
    >>> put(a='y')
    start wrapped func...
    yyyyyy
    >>> put(a=6, b='r')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "C:/Users/************/Desktop/Programmation/Python/Test/test.py", line 10, in wrapper
        raise TypeError("{} should be a {} object (got {}).".format(k, types[k].__name__, v.__class__.__name__))
    TypeError: a should be a str object (got int).
    >>> put(a='u')
    start wrapped func...
    uuuuuu
    >>> put('I', 7)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: wrapper() takes 0 positional arguments but 2 were given

    J'aimerai donc étendre cette propriété aux arguments positionnels de sorte que le dernier exemple soit correct. Mais je n'ai pas la moindre idée de comment implémenter ça.

    -
    Edité par Anonyme 20 septembre 2014 à 14:54:49

    • Partager sur Facebook
    • Partager sur Twitter
      20 septembre 2014 à 15:23:57

      Il faut déjà remplacer:

      def wrapper(**innerkwargs):

      par:

      def wrapper(*innerargs, **innerkwargs):

      Il faut ensuite changer le type d'argument de force() pour connaître l'ordre des arguments : utiliser une liste de tuples (arg_name, arg_type), ou un collections.OrderedDict(). Du coup, il te "suffit" ensuite de rechercher les arguments un à un et dans l'ordre pour connaître le type qu'ils doivent avoir. Pour les arguments nommés, aucun problème ne se pose.

      Par contre, cela devient encore un peu plus compliqué si tu veux que ta fonction prenne en charge les *args. Je n'y ai pas encore réfléchi.

      • Partager sur Facebook
      • Partager sur Twitter
      Anonyme
        20 septembre 2014 à 15:27:56

        Salut,
        Pour compléter, voici un décorateur extrait de la PEP 318 qui permet en plus de choisir plusieurs types pour un même argument :
        def accepts(*types):
            def check_accepts(f):
                assert len(types) == f.__code__.co_argcount
                def new_f(*args, **kwds):
                    for (a, t) in zip(args, types):
                        assert isinstance(a, t), "arg %r does not match %s" % (a,t)
                    return f(*args, **kwds)
                new_f.__name__ = f.__name__
                return new_f
            return check_accepts
        
        @accepts((int, float), (int, float))
        def m(a=10, b=5):
            print(a*b)
        >>> m(a=2.5, b=10)
        25.0
        >>> m(2.5, b=10)
        25.0
        >>> m(2)
        10

        -
        Edité par Anonyme 20 septembre 2014 à 15:30:11

        • Partager sur Facebook
        • Partager sur Twitter
        Anonyme
          20 septembre 2014 à 15:45:48

          @tatrats: Pas bête, je vais essayer ça.

          @Graphox: Super, merci . Je vais tout de même essayer de pas trop regarder le code pour le faire moi même et je compte ajouter une fonctionnalité qui permet de vérifier la longueur si le type est itérable.

          • Partager sur Facebook
          • Partager sur Twitter
          Anonyme
            20 septembre 2014 à 17:42:43

            Me revoilà avec une nouvelle question: Est-il possible de "convertir" des arguments positionnels en arguments à mots-clés ? Si oui comment ?

            Voilà le modèle de mon objectif:

            def decorateur(func):
                def wrapper(*args, **kwds):
                    print(magie()) # C'est là où je bloque
                    return func(*args, **kwds)
                return wrapper
            
            @decorateur
            def patate(a, b, c=9):
                print("{}\n{]\n{}".format(a, b, c))
            
            patate(3, 'x')
            # {'a': 3, 'b': 'x', 'c': 9}
            # 3
            # x
            # 9

            On peut poser la même question de manière générale: Comment accéder aux variables locales d'une fonction depuis l'extérieur de celle-ci ?

            Edit: j'aimeria n'importer aucun module pour ça (ça serait pas drôle sinon :p).

            -
            Edité par Anonyme 20 septembre 2014 à 17:45:36

            • Partager sur Facebook
            • Partager sur Twitter
              20 septembre 2014 à 17:54:45

              Il faut que tu ajoutes le nom de l'argument positionnel et sa valeur dans le dictionnaire `kwds` avant d'appeler la fonction décorée.

              Veux-tu créer un décorateur de vérification de types ou seulement l'utiliser ? Si tu veux, il existe déjà un décorateur qui vérifie les types des arguments annotés.
              • Partager sur Facebook
              • Partager sur Twitter
              Anonyme
                20 septembre 2014 à 18:14:38

                Veux-tu créer un décorateur de vérification de types ou seulement l'utiliser ?

                Les deux ! Je n'arriverai probablement pas à un résultat similaire à typecheck mais ce qui m'intéresse c'est de coder tout moi même.

                Il faut que tu ajoutes le nom de l'argument positionnel et sa valeur dans le dictionnaire `kwds` avant d'appeler la fonction décorée.

                Je sais bien mais je n'y parviens pas. J'ai essayé de bidouiller avec locals en vain. En fait, la difficulté vient du fait que le paramètre étoilé args est une liste, donc pas possible d'en tirer quelque nom de variable.

                • Partager sur Facebook
                • Partager sur Twitter
                  20 septembre 2014 à 18:32:02

                   Tu as deux possibilités pour récupérer le nom des arguments : soit utiliser le module inspect(je te laisse chercher car je ne sais plus trop comment il fonctionne), soit récupérer le nom des arguments, dans l'ordre, à partir de la fonction force(), comme je te l'avais proposé :

                  tatrats a écrit:

                  Il faut ensuite changer le type d'argument de force() pour connaître l'ordre des arguments : utiliser une liste de tuples (arg_name, arg_type), ou un collections.OrderedDict(). Du coup, il te "suffit" ensuite de rechercher les arguments un à un et dans l'ordre pour connaître <leur nom et> le type qu'ils doivent avoir.

                  Je ne comprends pas ce que tu veux faire avec locals()
                  Edit: ce qui t'intéresse dans inspect est inspect.signature

                  -
                  Edité par tatrats 20 septembre 2014 à 18:44:06

                  • Partager sur Facebook
                  • Partager sur Twitter
                  Anonyme
                    20 septembre 2014 à 20:09:13

                    @tatrats: J'ai (re)découvert les annotations, j'ai réussi à faire un typecheck similaire au lien que tu m'as donné (en moins développé certes ;)).

                    Sujet résolu !

                    • Partager sur Facebook
                    • Partager sur Twitter

                    Aide pour les décorateurs

                    × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                    × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                    • Editeur
                    • Markdown