web-dev-qa-db-fra.com

Pouvez-vous répertorier les arguments de mots clés reçus par une fonction?

J'ai un dict, dont j'ai besoin pour passer des clés/valeurs comme arguments de mots clés. Par exemple ..

d_args = {'kw1': 'value1', 'kw2': 'value2'}
example(**d_args)

Cela fonctionne très bien, mais s'il y a des valeurs dans le dict d_args qui ne sont pas acceptées par la fonction example, cela meurt évidemment .. Dites, si l'exemple de fonction est défini comme def example(kw2):

C'est un problème car je ne contrôle ni la génération de la fonction d_args, Ni la fonction example .. Ils proviennent tous deux de modules externes et example n'en accepte que certains des mots-clés-arguments du dict ..

Idéalement, je ferais juste

parsed_kwargs = feedparser.parse(the_url)
valid_kwargs = get_valid_kwargs(parsed_kwargs, valid_for = PyRSS2Gen.RSS2)
PyRSS2Gen.RSS2(**valid_kwargs)

Je vais probablement simplement filtrer le dict, à partir d'une liste d'arguments de mots clés valides, mais je me demandais: Existe-t-il un moyen de répertorier par programme les arguments de mots clés qu'une fonction spécifique prend?

98
dbr

Un peu plus agréable que d'inspecter directement l'objet de code et de travailler sur les variables est d'utiliser le module d'inspection.

>>> import inspect
>>> def func(a,b,c=42, *args, **kwargs): pass
>>> inspect.getargspec(func)
(['a', 'b', 'c'], 'args', 'kwargs', (42,))

Si vous voulez savoir si son appelable avec un ensemble particulier d'arguments, vous avez besoin des arguments sans défaut par défaut déjà spécifié. Ceux-ci peuvent être obtenus par:

def getRequiredArgs(func):
    args, varargs, varkw, defaults = inspect.getargspec(func)
    if defaults:
        args = args[:-len(defaults)]
    return args   # *args and **kwargs are not required, so ignore them.

Ensuite, une fonction pour dire ce que vous manquez dans votre dict est:

def missingArgs(func, argdict):
    return set(getRequiredArgs(func)).difference(argdict)

De même, pour vérifier les arguments non valides, utilisez:

def invalidArgs(func, argdict):
    args, varargs, varkw, defaults = inspect.getargspec(func)
    if varkw: return set()  # All accepted
    return set(argdict) - set(args)

Et donc un test complet s'il est appelable est:

def isCallableWithArgs(func, argdict):
    return not missingArgs(func, argdict) and not invalidArgs(func, argdict)

(Cela n'est valable que pour l'analyse d'arguments de python. Tout contrôle d'exécution pour les valeurs non valides dans kwargs ne peut évidemment pas être détecté.)

139
Brian

Cela affichera les noms de tous les arguments passables, ceux à mots-clés et non-mots-clés:

def func(one, two="value"):
    y = one, two
    return y
print func.func_code.co_varnames[:func.func_code.co_argcount]

C'est parce que d'abord co_varnames sont toujours des paramètres (viennent ensuite les variables locales, comme y dans l'exemple ci-dessus).

Alors maintenant, vous pourriez avoir une fonction:

def getValidArgs(func, argsDict):
    '''Return dictionary without invalid function arguments.'''
    validArgs = func.func_code.co_varnames[:func.func_code.co_argcount]
    return dict((key, value) for key, value in argsDict.iteritems() 
                if key in validArgs)

Que vous pourriez ensuite utiliser comme ceci:

>>> func(**getValidArgs(func, args))

MODIFIER : Un petit ajout: si vous avez vraiment besoin uniquement d'arguments de mot-clé d'une fonction, vous pouvez utiliser le func_defaults attribut pour les extraire:

def getValidKwargs(func, argsDict):
    validArgs = func.func_code.co_varnames[:func.func_code.co_argcount]
    kwargsLen = len(func.func_defaults) # number of keyword arguments
    validKwargs = validArgs[-kwargsLen:] # because kwargs are last
    return dict((key, value) for key, value in argsDict.iteritems() 
                if key in validKwargs)

Vous pouvez maintenant appeler votre fonction avec des arguments connus, mais des kwargs extraits, par exemple:

func(param1, param2, **getValidKwargs(func, kwargsDict))

Cela suppose que func n'utilise pas *args ou **kwargs la magie dans sa signature.

30
DzinX

Dans Python 3.0:

>>> import inspect
>>> import fileinput
>>> print(inspect.getfullargspec(fileinput.input))
FullArgSpec(args=['files', 'inplace', 'backup', 'bufsize', 'mode', 'openhook'],
varargs=None, varkw=None, defaults=(None, 0, '', 0, 'r', None), kwonlyargs=[], 
kwdefaults=None, annotations={})
6
jfs

Pour une solution Python 3, vous pouvez utiliser inspect.signature et filtrez selon le type de paramètres que vous aimeriez connaître.

Prendre un exemple de fonction avec les paramètres positionnel ou mot clé, mot clé uniquement, var positionnel et var mot clé:

def spam(a, b=1, *args, c=2, **kwargs):
    print(a, b, args, c, kwargs)

Vous pouvez créer un objet signature pour celui-ci:

from inspect import signature
sig =  signature(spam)

puis filtrez avec une liste de compréhension pour trouver les détails dont vous avez besoin:

>>> # positional or keyword
>>> [p.name for p in sig.parameters.values() if p.kind == p.POSITIONAL_OR_KEYWORD]
['a', 'b']
>>> # keyword only
>>> [p.name for p in sig.parameters.values() if p.kind == p.KEYWORD_ONLY]
['c']

et, de même, pour les positions var utilisant p.VAR_POSITIONAL et mot clé var avec VAR_KEYWORD.

De plus, vous pouvez ajouter une clause au if pour vérifier si une valeur par défaut existe en vérifiant si p.default équivaut à p.empty.

4

Extension de la réponse de DzinX:

argnames = example.func_code.co_varnames[:func.func_code.co_argcount]
args = dict((key, val) for key,val in d_args.iteritems() if key in argnames)
example(**args)
3
Claudiu