É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")?
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)>
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.
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]
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.
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}
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
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.
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)
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}
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={})
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.
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'})
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
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')