Obteniendo nombres de parámetros de métodos en Python

Dada la función de Python:

def aMethod(arg1, arg2): pass 

¿Cómo puedo extraer el número y los nombres de los argumentos. Es decir, dado que tengo una referencia a func, quiero que la función [algo] regrese (“arg1”, “arg2”).

El escenario de uso para esto es que tengo un decorador, y deseo usar los argumentos del método en el mismo orden en que aparecen para la función real como una clave. Es decir, ¿cómo se vería el decorador que imprimió “a, b” cuando llamo aMethod (“a”, “b”)?

Eche un vistazo al módulo de inspección : esto hará la inspección de las diversas propiedades del objeto de código por usted.

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

Los otros resultados son el nombre de las variables * args y ** kwargs, y los valores predeterminados proporcionados. es decir.

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

Tenga en cuenta que algunos callables pueden no ser introspectibles en ciertas implementaciones de Python. Por ejemplo, en CPython, algunas funciones incorporadas definidas en C no proporcionan metadatos sobre sus argumentos. Como resultado, obtendrá ValueError en el caso de que utilice inspect.getfullargspec() con una función incorporada.

Desde Python 3.3, puede usar también inspect.signature () para conocer la firma de llamada de un objeto que se puede llamar:

 >>> inspect.signature(foo)  

En CPython, el número de argumentos es

 aMethod.func_code.co_argcount 

y sus nombres están al principio de

 aMethod.func_code.co_varnames 

Estos son detalles de implementación de CPython, por lo que probablemente esto no funcione en otras implementaciones de Python, como IronPython y Jython.

Una forma portátil de admitir argumentos “de paso” es definir su función con la func(*args, **kwargs) firma func(*args, **kwargs) . Esto se usa mucho, por ejemplo, en matplotlib, donde la capa API externa pasa muchos argumentos de palabras clave a la API de nivel inferior.

En un método de decoración, puede enumerar los argumentos del método original de esta manera:

 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 los **kwargs son importantes para ti, entonces será un poco complicado:

  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() 

Ejemplo:

 @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] 

Aquí hay algo que creo que funcionará para lo que quieras, usando un decorador.

 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") 

Ejecutarlo, producirá el siguiente resultado:

 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. 

Creo que lo que estás buscando es el método de los lugareños.

 In [6]: def test(a, b):print locals() ...: In [7]: test(1,2) {'a': 1, 'b': 2} 

La versión de Python 3 es:

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

El método devuelve un diccionario que contiene args y kwargs.

Python 3.5+:

DeprecationWarning: inspect.getargspec () está en desuso, use inspect.signature () en su lugar

Así que anteriormente:

 func_args = inspect.getargspec(function).args 

Ahora:

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

Probar:

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

Dado que tenemos la función ‘función’ que toma el argumento ‘arg’, esto se evaluará como Verdadero, de lo contrario como Falso.

Ejemplo desde la consola de 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 

En Python 3. + con el objeto de Signature a la mano, una forma fácil de obtener un mapeo entre los nombres de los argumentos y los valores, ¡es usando el método bind() la Firma!

Por ejemplo, aquí hay un decorador para imprimir un mapa como ese:

 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} 

Devuelve una lista de nombres de argumentos, se ocupa de los parciales y funciones regulares:

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

Actualización para la respuesta de Brian :

Si una función en Python 3 tiene argumentos de solo palabras clave, entonces necesita usar inspect.getfullargspec :

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

produce esto:

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

En Python 3, a continuación se **kwargs hacer *args y **kwargs en un dict (use OrderedDict para python <3.6 para mantener las órdenes de 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 

Para actualizar un poco la respuesta de Brian , ahora hay un buen backport de inspect.signature que puede usar en versiones anteriores de python: funcsigs . Así que mi preferencia personal iría por

 try: # python 3.3+ from inspect import signature except ImportError: from funcsigs import signature def aMethod(arg1, arg2): pass sig = signature(aMethod) print(sig) 

Para divertirse, si está interesado en jugar con objetos Signature e incluso crear funciones con firmas aleatorias de forma dinámica, puede echar un vistazo a mi proyecto makefun .

¿Qué hay de dir() y vars() ahora?

Parece hacer exactamente lo que se pregunta super simplemente …

Debe ser llamado desde dentro del scope de la función.

Pero tenga cuidado de no devolver todas las variables locales, así que asegúrese de hacerlo al principio de la función si es necesario.

También tenga en cuenta que, como se señaló en los comentarios, esto no permite que se haga desde fuera del scope. Así que no es exactamente el escenario de OP, pero aún coincide con el título de la pregunta. De ahí mi respuesta.