2010-04-20 12 views
69

Dado un objeto de función, ¿cómo puedo obtener su firma? Por ejemplo, para:¿Cómo puedo leer la firma de una función, incluidos los valores de argumento predeterminados?

def myMethod(firt, second, third='something'): 
    pass 

me gustaría llegar "myMethod(firt, second, third='something')".

+3

¿Puede explicar su pregunta específica y quizás dar un ejemplo con el resultado esperado? – jhwist

+0

Es de suponer que está buscando funcionalidad en Python o bibliotecas de terceros que devolverán la firma de un método (nombres y tipos de parámetros y valor de retorno) dado el nombre del método. –

+1

¿Firma como en cómo llamarla y tal? Pruebe 'help (yourmethod)' p. Ej. 'help (map)' –

Respuesta

108
import inspect 

def foo(a, b, x='blah'): 
    pass 

print(inspect.getargspec(foo)) 
# ArgSpec(args=['a', 'b', 'x'], varargs=None, keywords=None, defaults=('blah',)) 

Sin embargo, tenga en cuenta que inspect.getargspec() está obsoleta desde Python 3.0.

Python 3.0--3.4 recomienda inspect.getfullargspec().

Python 3.5+ recomienda inspect.signature().

+0

AttributeError: el objeto 'module' no tiene el atributo 'getargspec' –

+3

@Spi, estás llamando 'inspect.getargspec' en un módulo, no una función. –

+0

Gracias, el problema fue con Eclipse que no vio el módulo inspeccionar –

7

Intente llamar al help en un objeto para obtener más información al respecto.

>>> foo = [1, 2, 3] 
>>> help(foo.append) 
Help on built-in function append: 

append(...) 
    L.append(object) -- append object to end 
12
#! /usr/bin/env python 

import inspect 
from collections import namedtuple 

DefaultArgSpec = namedtuple('DefaultArgSpec', 'has_default default_value') 

def _get_default_arg(args, defaults, arg_index): 
    """ Method that determines if an argument has default value or not, 
    and if yes what is the default value for the argument 

    :param args: array of arguments, eg: ['first_arg', 'second_arg', 'third_arg'] 
    :param defaults: array of default values, eg: (42, 'something') 
    :param arg_index: index of the argument in the argument array for which, 
    this function checks if a default value exists or not. And if default value 
    exists it would return the default value. Example argument: 1 
    :return: Tuple of whether there is a default or not, and if yes the default 
    value, eg: for index 2 i.e. for "second_arg" this function returns (True, 42) 
    """ 
    if not defaults: 
     return DefaultArgSpec(False, None) 

    args_with_no_defaults = len(args) - len(defaults) 

    if arg_index < args_with_no_defaults: 
     return DefaultArgSpec(False, None) 
    else: 
     value = defaults[arg_index - args_with_no_defaults] 
     if (type(value) is str): 
      value = '"%s"' % value 
     return DefaultArgSpec(True, value) 

def get_method_sig(method): 
    """ Given a function, it returns a string that pretty much looks how the 
    function signature would be written in python. 

    :param method: a python method 
    :return: A string similar describing the pythong method signature. 
    eg: "my_method(first_argArg, second_arg=42, third_arg='something')" 
    """ 

    # The return value of ArgSpec is a bit weird, as the list of arguments and 
    # list of defaults are returned in separate array. 
    # eg: ArgSpec(args=['first_arg', 'second_arg', 'third_arg'], 
    # varargs=None, keywords=None, defaults=(42, 'something')) 
    argspec = inspect.getargspec(method) 
    arg_index=0 
    args = [] 

    # Use the args and defaults array returned by argspec and find out 
    # which arguments has default 
    for arg in argspec.args: 
     default_arg = _get_default_arg(argspec.args, argspec.defaults, arg_index) 
     if default_arg.has_default: 
      args.append("%s=%s" % (arg, default_arg.default_value)) 
     else: 
      args.append(arg) 
     arg_index += 1 
    return "%s(%s)" % (method.__name__, ", ".join(args)) 


if __name__ == '__main__': 
    def my_method(first_arg, second_arg=42, third_arg='something'): 
     pass 

    print get_method_sig(my_method) 
    # my_method(first_argArg, second_arg=42, third_arg="something") 
+0

¿Alguna explicación sobre lo que se supone que debe hacer esto? – grantmcconnaughey

+0

Agregó comentarios a la muestra del código, espero que ayude. –

+0

¡Hermoso, gracias! – grantmcconnaughey

21

Podría decirse que la forma más fácil de encontrar la firma de una función sería help(function):

>>> def function(arg1, arg2="foo", *args, **kwargs): pass 
>>> help(function) 
Help on function function in module __main__: 

function(arg1, arg2='foo', *args, **kwargs) 

Además, en Python 3 se añadió un método para el módulo inspect llamada signature, que está diseñado para representar la signature of a callable object and its return annotation:

>>> from inspect import signature 
>>> def foo(a, *, b:int, **kwargs): 
...  pass 

>>> sig = signature(foo) 

>>> str(sig) 
'(a, *, b:int, **kwargs)' 

>>> str(sig.parameters['b']) 
'b:int' 

>>> sig.parameters['b'].annotation 
<class 'int'> 
+1

Perfecto. Todo lo que realmente quería era descargar una firma legible para una función. – ArtOfWarfare

+2

'inspect.signature' también está disponible para Python 2 a través del proyecto backport' funcsigs': https://pypi.python.org/pypi/funcsigs – ncoghlan

5

Tal vez un poco tarde a la fiesta, pero si también desea mantener el orden de la una los argumentos y sus valores predeterminados, luego puede usar el Abstract Syntax Tree module (ast).

Aquí es una prueba de concepto (cuidado con el código para ordenar los argumentos y unirlas a sus valores predeterminados sin duda puede ser mejorado/hecho más clara):

import ast 

for class_ in [c for c in module.body if isinstance(c, ast.ClassDef)]: 
    for method in [m for m in class_.body if isinstance(m, ast.FunctionDef)]: 
     args = [] 
     if method.args.args: 
      [args.append([a.col_offset, a.id]) for a in method.args.args] 
     if method.args.defaults: 
      [args.append([a.col_offset, '=' + a.id]) for a in method.args.defaults] 
     sorted_args = sorted(args) 
     for i, p in enumerate(sorted_args): 
      if p[1].startswith('='): 
       sorted_args[i-1][1] += p[1] 
     sorted_args = [k[1] for k in sorted_args if not k[1].startswith('=')] 

     if method.args.vararg: 
      sorted_args.append('*' + method.args.vararg) 
     if method.args.kwarg: 
      sorted_args.append('**' + method.args.kwarg) 

     signature = '(' + ', '.join(sorted_args) + ')' 

     print method.name + signature 
3

Si todo lo que estamos tratando de hacer es imprimir la función luego usa pydoc.

import pydoc  

def foo(arg1, arg2, *args, **kwargs):                  
    '''Some foo fn'''                      
    pass                         

>>> print pydoc.render_doc(foo).splitlines()[2] 
foo(arg1, arg2, *args, **kwargs) 

Si está intentando analizar realmente la firma de la función, utilice argspec del módulo de inspección. Tuve que hacer eso al validar la función de script de gancho de un usuario en un marco general.

Cuestiones relacionadas