web-dev-qa-db-fra.com

Obtenir les noms de paramètre de méthode en Python

Étant donné la fonction Python:

def aMethod(arg1, arg2):
    pass

Comment puis-je extraire le nombre et les noms des arguments. C'est-à-dire, étant donné que j'ai une référence à func, je veux que le func. [Quelque chose] retourne ("arg1", "arg2").

Le scénario d'utilisation de ceci est que j'ai un décorateur et que je souhaite utiliser les arguments de la méthode dans le même ordre qu'ils apparaissent pour la fonction réelle en tant que clé. C'est-à-dire, à quoi ressemblerait le décorateur qui a imprimé "a, b" lorsque j'appelle aMethod ("a", "b")?

194
Staale

Jetez un coup d’œil au module inspect - il inspectera pour vous les différentes propriétés des objets de code.

>>> inspect.getfullargspec(aMethod)
(['arg1', 'arg2'], None, None, None)

Les autres résultats sont le nom des variables * args et ** kwargs, ainsi que les valeurs par défaut fournies. c'est à dire.

>>> def foo(a,b,c=4, *arglist, **keywords): pass
>>> inspect.getfullargspec(foo)
(['a', 'b', 'c'], 'arglist', 'keywords', (4,))

Notez que certains callables peuvent ne pas être introspectables dans certaines implémentations de Python. Par exemple, dans CPython, certaines fonctions intégrées définies en C ne fournissent aucune métadonnée à propos de leurs arguments. En conséquence, vous obtiendrez ValueError si vous utilisez inspect.getfullargspec() avec une fonction intégrée.

Depuis Python 3.3, vous pouvez également utiliser le inspect.signature () afin de connaître la signature d'appel d'un objet appelable:

>>> inspect.signature(foo)
<Signature (a, b, c=4, *arglist, **keywords)>
308
Brian

Dans CPython, le nombre d'arguments est

aMethod.func_code.co_argcount

et leurs noms sont au début de

aMethod.func_code.co_varnames

Ce sont des détails d'implémentation de CPython, donc cela ne fonctionne probablement pas dans d'autres implémentations de Python, telles que IronPython et Jython.

Un moyen portable d'admettre des arguments "pass-through" consiste à définir votre fonction avec la signature func(*args, **kwargs). Ceci est beaucoup utilisé dans par exemple matplotlib, où la couche API externe transmet beaucoup d'arguments de mots clés à l'API de niveau inférieur.

92
Jouni K. Seppänen

Dans une méthode decorator, vous pouvez lister les arguments de la méthode originale de cette façon:

import inspect, itertools 

def my_decorator():

        def decorator(f):

            def wrapper(*args, **kwargs):

                # if you want arguments names as a list:
                args_name = inspect.getargspec(f)[0]
                print(args_name)

                # if you want names and values as a dictionary:
                args_dict = dict(itertools.izip(args_name, args))
                print(args_dict)

                # if you want values as a list:
                args_values = args_dict.values()
                print(args_values)

Si les **kwargs sont importants pour vous, alors ce sera un peu compliqué:

        def wrapper(*args, **kwargs):

            args_name = list(OrderedDict.fromkeys(inspect.getargspec(f)[0] + kwargs.keys()))
            args_dict = OrderedDict(list(itertools.izip(args_name, args)) + list(kwargs.iteritems()))
            args_values = args_dict.values()

Exemple:

@my_decorator()
def my_function(x, y, z=3):
    pass


my_function(1, y=2, z=3, w=0)
# prints:
# ['x', 'y', 'z', 'w']
# {'y': 2, 'x': 1, 'z': 3, 'w': 0}
# [1, 2, 3, 0]
21
Mehdi Behrooz

Voici quelque chose qui, selon moi, fonctionnera pour ce que vous voulez, en utilisant un décorateur.

class LogWrappedFunction(object):
    def __init__(self, function):
        self.function = function

    def logAndCall(self, *arguments, **namedArguments):
        print "Calling %s with arguments %s and named arguments %s" %\
                      (self.function.func_name, arguments, namedArguments)
        self.function.__call__(*arguments, **namedArguments)

def logwrap(function):
    return LogWrappedFunction(function).logAndCall

@logwrap
def doSomething(spam, eggs, foo, bar):
    print "Doing something totally awesome with %s and %s." % (spam, eggs)


doSomething("beans","rice", foo="wiggity", bar="wack")

Exécutez-le, vous obtiendrez le résultat suivant:

C:\scripts>python decoratorExample.py
Calling doSomething with arguments ('beans', 'rice') and named arguments {'foo':
 'wiggity', 'bar': 'wack'}
Doing something totally awesome with beans and rice.
13
hlzr

Je pense que ce que vous recherchez, c'est la méthode des locaux - 


In [6]: def test(a, b):print locals()
   ...: 

In [7]: test(1,2)              
{'a': 1, 'b': 2}
11
Damian

Python 3.5+:

DeprecationWarning: inspect.getargspec () est obsolète, utilisez inspect.signature () à la place

Alors précédemment:

func_args = inspect.getargspec(function).args

À présent:

func_args = list(inspect.signature(function).parameters.keys())

Tester:

'arg' in list(inspect.signature(function).parameters.keys())

Etant donné que nous avons la fonction 'fonction' qui prend l'argument 'arg', elle sera évaluée comme True, sinon comme False.

Exemple depuis la console Python:

Python 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 07:18:10) [MSC v.1900 32 bit (Intel)] on win32
>>> import inspect
>>> 'iterable' in list(inspect.signature(sum).parameters.keys())
True
5
Peter Majko

La version Python 3 est:

def _get_args_dict(fn, args, kwargs):
    args_names = fn.__code__.co_varnames[:fn.__code__.co_argcount]
    return {**dict(Zip(args_names, args)), **kwargs}

La méthode retourne un dictionnaire contenant à la fois args et kwargs.

4
argaen

Renvoie une liste de noms d'arguments, prend en charge les partiels et les fonctions habituelles:

def get_func_args(f):
    if hasattr(f, 'args'):
        return f.args
    else:
        return list(inspect.signature(f).parameters)
2
lovesh

En Python 3. +, avec l’objet Signature à portée de main, un moyen simple d’obtenir un mappage entre les noms d’argument et les valeurs, utilise la méthode bind() de la signature!

Par exemple, voici un décorateur pour imprimer une carte comme celle-ci:

import inspect

def decorator(f):
    def wrapper(*args, **kwargs):
        bound_args = inspect.signature(f).bind(*args, **kwargs)
        bound_args.apply_defaults()
        print(dict(bound_args.arguments))

        return f(*args, **kwargs)

    return wrapper

@decorator
def foo(x, y, param_with_default="bars", **kwargs):
    pass

foo(1, 2, extra="baz")
# This will print: {'kwargs': {'extra': 'baz'}, 'param_with_default': 'bars', 'y': 2, 'x': 1}
2
Kfir Eisner

Mise à jour pour Réponse de Brian :

Si une fonction dans Python 3 a des arguments composés uniquement de mots clés, vous devez utiliser inspect.getfullargspec:

def yay(a, b=10, *, c=20, d=30):
    pass
inspect.getfullargspec(yay)

donne ceci:

FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=(10,), kwonlyargs=['c', 'd'], kwonlydefaults={'c': 20, 'd': 30}, annotations={})
2
user1981531

Pour mettre à jour un peu La réponse de Brian , il existe maintenant un backport Nice de inspect.signature que vous pouvez utiliser dans les anciennes versions de python: funcsigs . Donc, ma préférence personnelle irait pour

try:  # python 3.3+
    from inspect import signature
except ImportError:
    from funcsigs import signature

def aMethod(arg1, arg2):
    pass

sig = signature(aMethod)
print(sig)

Pour le plaisir, si vous souhaitez jouer avec les objets Signature et même créer des fonctions avec des signatures aléatoires de façon dynamique, vous pouvez consulter mon makefun project.

0
smarie

Voici un autre moyen d'obtenir les paramètres de la fonction sans utiliser de module.

def get_parameters(func):
    keys = func.__code__.co_varnames[:func.__code__.co_argcount][::-1]
    sorter = {j: i for i, j in enumerate(keys[::-1])} 
    values = func.__defaults__[::-1]
    kwargs = {i: j for i, j in Zip(keys, values)}
    sorted_args = Tuple(
        sorted([i for i in keys if i not in kwargs], key=sorter.get)
    )
    sorted_kwargs = {}
    for i in sorted(kwargs.keys(), key=sorter.get):
        sorted_kwargs[i] = kwargs[i]      
    return sorted_args, sorted_kwargs


def f(a, b, c="hello", d="world"): var = a


print(get_parameters(f))

Sortie:

(('a', 'b'), {'c': 'hello', 'd': 'world'})
0
dildeolupbiten

En python 3, ci-dessous consiste à transformer *args et **kwargs en une dict (utilisez OrderedDict pour python <3.6 pour maintenir les ordres dict):

from functools import wraps

def display_param(func):
    @wraps(func)
    def wrapper(*args, **kwargs):

        param = inspect.signature(func).parameters
        all_param = {
            k: args[n] if n < len(args) else v.default
            for n, (k, v) in enumerate(param.items()) if k != 'kwargs'
        }
        all_param .update(kwargs)
        print(all_param)

        return func(**all_param)
    return wrapper
0
Alpha

inspect.signature est très lent. Le moyen le plus rapide est

def f(a, b=1, *args, c, d=1, **kwargs):
   pass

f_code = f.__code__
f_code.co_varnames[:f_code.co_argcount + f_code.co_kwonlyargcount]  # ('a', 'b', 'c', 'd')
0
Nikolay Makhalin