2008-10-20 8 views
167

Dada la función de Python:Obtención de nombres de parámetros en el método pitón

def aMethod(arg1, arg2): 
    pass 

¿Cómo puedo extraer el número y los nombres de los argumentos. Es decir. teniendo en cuenta que tengo una referencia a Func, quiero que el func. [algo] para volver ("arg1", "arg2")

El escenario de uso de esto es que tengo un decorador, y me gustaría utilizar el métodos de argumentos en el mismo orden en que aparecen para la función real como una clave. Es decir. ¿cómo sería el aspecto decorador que se imprime "a, b" cuando llamo aMethod ("a", "b")

+1

Para una lista diferente de respuestas a una pregunta casi idénticos, véase [este otro post StackOverflow] (http://stackoverflow.com/questions/582056/getting-list-of-parameters-inside-python-function) –

+1

Su título es engañoso: cuando uno dice 'método' con la palabra 'función', uno generalmente piensa en un método de clase. Para la función, la respuesta seleccionada (de Jouni K. Seppanen) es buena. Pero para el método (clase), no está funcionando y se debe usar la solución de inspección (de Brian). –

Respuesta

278

Tome una mirada en el módulo de inspect - esto va a hacer la inspección de las diversas propiedades de los objetos de código para usted.

>>> inspect.getargspec(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.getargspec(foo) 
(['a', 'b', 'c'], 'arglist', 'keywords', (4,)) 

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

Como Python 3.3, se puede utilizar también la inspect.signature() el fin de conocer la firma de llamada de un objeto invocable:

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

ver también http://stackoverflow.com/a/3999574/288875 para examinar callables en general –

+17

¿Cómo es posible que el código sepa que el parámetro predeterminado '(4,)' corresponde al parámetro de palabra clave 'c' específicamente? – fatuhoku

+47

@fatuhoku Me preguntaba lo mismo. Resulta que no es ambiguo ya que solo puede agregar argumentos predeterminados al final en un bloque contiguo. De los documentos: "si esta tupla tiene n elementos, corresponden a los últimos n elementos enumerados en args" – Soverman

86

En CPython, el número de argumentos es

aMethod.func_code.co_argcount 

y sus nombres están en el a partir de

aMethod.func_code.co_varnames 

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

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

+0

co_varnames funciona con Python estándar, pero este método no es preferible ya que también mostrará los argumentos internos. – MattK

+11

¿Por qué no utilizar aMethod.func_code.co_varnames [: aMethod.func_code.co_argcount]? – hochl

+12

'func_code' ahora se puede encontrar como' __code__' –

13

Aquí es algo que creo que va a trabajar para lo que quiere, 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, se 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. 
10

Creo que lo que estás buscando es el método de los locales -


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

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

Esto es inútil fuera de una función que es el contexto de interés aquí (decorador). –

+3

En realidad exactamente lo que estaba buscando, aunque no es la respuesta a la pregunta aquí. – javabeangrinder

19

En un método decorador, se puede enumerar 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 el **kwargs son importantes para usted, 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] 
3

Dado que las respuestas son un poco viejo y tenía que hacer algunas modificaciones para lo que es trabajo para python3, estoy añadiendo mi versión:

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 ambos argumentos y kwargs.

-1

¿Qué pasa con dir() y vars() ahora?

Parece haciendo exactamente lo que se pide simplemente súper ...

(Desde el interior de la función/método)

+0

dir() devuelve la lista de todos los nombres de variable ['var1', 'var2'], vars() devuelve diccionario en forma {'var1': 0, 'var2': 'algo'} dentro del alcance local actual. Si alguien quiere usar nombres de variables de argumento más adelante en la función, deberían guardar en otra variable local, porque al llamarlo más tarde en la función donde podrían declarar otras variables locales se "contaminará" esta lista. En el caso de que quieran usarlo fuera de la función, deben ejecutar la función al menos una vez y guardarla en la variable global. Entonces es mejor usar el módulo de inspección. –

1

Actualización de la respuesta de Brian. Si la función en Python 3 tiene palabras clave sólo argumentos, entonces usted necesita para utilizar 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={}) 
2

devuelve una lista de nombres de argumentos, se encarga de parciales y funciones regulares :

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

Python 3.5+

D eprecationWarning: inspect.getargspec() está en desuso, el uso inspect.signature() en lugar

Así anteriormente:

func_args = inspect.getargspec(function).args 

Ahora:

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

Para 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 de consola 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 
2

en Python 3. + con el objeto Signature a mano, una manera fácil de obtener una asignación entre los nombres args a los valores, es utilizando el método de la Firma bind()!

Por ejemplo, aquí es un decorador para imprimir un mapa de esa manera:

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 wil print: {'kwargs': {'extra': 'baz'}, 'param_with_default': 'bars', 'y': 2, 'x': 1} 
Cuestiones relacionadas