3
votes

Arguments de fonction validés en Python

J'ai une fonction

def decorator(func):
    def new_func(*args, **kwargs):
        if (func.__name__ == 'func'):
            a = ?
            b = ?
            c = ?
            d = ?
        else:
            a = ?
            b = ?
            c = ?
            d = ?
        log_to_system(a, b, c, d)
        return func(*args, **kwargs)
return new_func

et j'essaie d'écrire un décorateur qui comprend les arguments et enregistre certains d'entre eux sur un autre système.

def func(a,b,c,d):
    ...


6 commentaires

func (2,3,4, a = 1) est-il un moyen valide d'utiliser func ? Si oui, b est-il égal à 2?


Quels quatre arguments recherchez-vous dans new_func ? Etes-vous sûr que new_func doit être aussi général, si vous avez 4 arguments spécifiques en tête?


@chepner oui, cela doit être général car je dois utiliser ce décorateur sur différentes fonctions et extraire des sous-ensembles de paramètres que je peux connecter à un système. Je ne veux pas tout enregistrer.


Sonne comme une mauvaise idée d'avoir un décorateur se comporter différemment pour différentes fonctions de l'OMI. Pourriez-vous utiliser deux décorateurs différents, ou utiliser un décorateur qui prend un argument pour lui dire quel style utiliser (et donc quelle fonction encapsulée renvoyer)?


Vous allez probablement devoir sortir le module inspect et commencer à fouiller dans l'argument func du décorateur.


Copie possible de Décorateur pour imprimer les détails de l'appel de fonction - noms des paramètres et valeurs effectives


3 Réponses :


2
votes

Une approche simple consiste à faire en sorte que votre fonction log_to_system accepte des paramètres variables et des paramètres de mots-clés variables en plus des paramètres connus qu'elle enregistrera réellement, de sorte que vous puissiez simplement transmettre les arguments variables et la variable arguments de mots clés de la fonction décorée vers log_to_system et laissez l'interpréteur extraire les paramètres a , b , c et d pour vous:

1 2 3 4

Ceci génère:

import inspect

def log_to_system(a, b, c, d):
    print(a, b, c, d)

def decorator(func):
    sig = inspect.signature(func)
    def new_func(*args, **kwargs):
        arguments = sig.bind(*args, **kwargs).arguments
        log_to_system(**{k: arguments[k] for k in log_to_system.__code__.co_varnames})
        return func(*args, **kwargs)
    return new_func

@decorator
def func(a, b, c, d, e):
    pass

func(1, 2, c=3, d=4, e=5)

Vous pouvez également utiliser inspect.signature pour obtenir un dict d'arguments après avoir lié les arguments de variable et les arguments de mot-clé donnés à la signature de la fonction décorée, afin que vous puissiez appeler log_to_system avec seulement les paramètres dont il a besoin:

XXX

Ceci génère:

1 2 3 4


1 commentaires

C'est une idée intéressante. J'accepterai cette solution pour l'instant car elle résout mon problème.



1
votes

Vous pouvez utiliser inspect , exemple fonctionnel:

import inspect


def deco(func):
    signature = inspect.signature(func)

    def _deco(*args, **kwargs):
        binded = signature.bind(*args, **kwargs)
        arguments = binded.arguments  # OrderedDict
        print(arguments.items())
        return func(*args, **kwargs)

    return _deco


@deco
def foo(a, b, c, d):
    pass


@deco
def bar(d, c, b, a):
    pass


foo(1, 2, c=3, d=4)  # odict_items([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
bar(1, a=2, b=3, c=4)  # odict_items([('d', 1), ('c', 4), ('b', 3), ('a', 2)])


0 commentaires

0
votes

Vous pouvez utiliser ce décorateur général pour vider les valeurs des paramètres:

def dump_args(func):
    # Decorator to print function call details - parameters names and effective values
    def wrapper(*func_args, **func_kwargs):
        arg_names = func.__code__.co_varnames[:func.__code__.co_argcount]
        args = func_args[:len(arg_names)]
        defaults = func.__defaults__ or ()
        args = args + defaults[len(defaults) - (func.__code__.co_argcount - len(args)):]
        params = list(zip(arg_names, args))
        args = func_args[len(arg_names):]
        if args: params.append(('args', args))
        if func_kwargs: params.append(('kwargs', func_kwargs))
        return func(*func_args, **func_kwargs)
    return wrapper

Les crédits appartiennent à @aliteralmind, il a suggéré ce décorateur dans ce StackOverflow post .


2 commentaires

Si la solution consiste simplement à utiliser directement le résultat d'une autre question SO, il serait préférable de marquer cette question comme un double plutôt que de fournir une réponse en double.


Merci, j'ai ajouté un drapeau en double.