Come posso leggere la firma di una funzione inclusi i valori degli argomenti predefiniti?


129

Dato un oggetto funzione, come posso ottenere la sua firma? Ad esempio, per:

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

Mi piacerebbe ottenere "myMethod(firt, second, third='something')".


3
Potete per favore approfondire la vostra domanda specifica e forse fare un esempio con il risultato atteso?
jhwist,

Presumibilmente sta cercando funzionalità in Python o librerie di terze parti che restituiranno la firma di un metodo (nomi e tipi di parametri e valore restituito) dato il nome del metodo.
Michael Petrotta,

1
Firma come in come chiamarlo e così? Prova help(yourmethod)ad esempiohelp(map)
Nick T,

Risposte:


187
import inspect

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

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

Tuttavia, si noti che inspect.getargspec()è obsoleto da Python 3.0.

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

Python 3.5+ consiglia inspect.signature().


AttributeError: l'oggetto 'module' non ha attributi 'getargspec'
Spì

3
@Spi, stai chiamando inspect.getargspecun modulo, non una funzione.
Mike Graham,

Grazie, il problema era con Eclipse che non vedeva il modulo di ispezione
Spì

Se una funzione ha annotazioni di argomenti o argomenti di sole parole chiave (= se stai usando Python 3) devi getfullargspecinvece chiamare . ( ValueError: Function has keyword-only arguments or annotations, use getfullargspec() API which can support them)
badp,

2
@darth_coder: in Python2, getargspecgenera TypeErrorse l'input non viene riconosciuto come una funzione Python , ovvero una funzione implementata in Python. In CPython, Exception.__init__è implementato in C, quindi il TypeError. Dovrai controllare il codice sorgente per capire la firma della chiamata. In Python3, getargspecè implementato in modo diverso e inspect.getargspec(Exception.__init__)restituisce ArgSpecun'istanza.
unutbu

44

Probabilmente il modo più semplice per trovare la firma per una funzione sarebbe 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)

Inoltre, in Python 3 è stato aggiunto un metodo al inspectmodulo chiamato signature, progettato per rappresentare la firma di un oggetto richiamabile e la sua annotazione di ritorno :

>>> 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'>

3
inspect.signatureè disponibile anche per Python 2 tramite il funcsigsprogetto backport: pypi.python.org/pypi/funcsigs
ncoghlan,

14
#! /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")

Qualche spiegazione su cosa dovrebbe fare?
grantmcconnaughey,

1
Aggiunti commenti all'esempio di codice, spero che sia d'aiuto.
Arup Malakar,

Roba adorabile. Sarebbe ancora meglio se potessi def foo(a, *, b:int, **kwargs)foo(4, b=3.3)
adattarlo

8

Prova a chiamare helpun oggetto per scoprirlo.

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

append(...)
    L.append(object) -- append object to end

7

Forse un po 'in ritardo alla festa, ma se vuoi anche mantenere l'ordine degli argomenti e dei loro valori predefiniti , puoi usare il modulo Albero di sintassi astratto (ast) .

Ecco una prova di concetto (attenzione al codice per ordinare gli argomenti e abbinarli ai loro valori predefiniti può sicuramente essere migliorato / reso più chiaro):

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

Si noti che gli argomenti non predefiniti non possono seguire gli argomenti predefiniti , quindi possiamo semplicemente abbinarli dalla coda?
Evgeni Sergeev,

5

Se tutto ciò che stai cercando di fare è stampare la funzione, 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)

Se si sta tentando di analizzare effettivamente la firma della funzione, utilizzare argspec del modulo di ispezione. Ho dovuto farlo quando ho convalidato la funzione di script hook di un utente in un framework generale.


3

Codice di esempio:

import inspect
from collections import OrderedDict


def get_signature(fn):
    params = inspect.signature(fn).parameters
    args = []
    kwargs = OrderedDict()
    for p in params.values():
        if p.default is p.empty:
            args.append(p.name)
        else:
            kwargs[p.name] = p.default
    return args, kwargs


def test_sig():
    def fn(a, b, c, d=3, e="abc"):
        pass

    assert get_signature(fn) == (
        ["a", "b", "c"], OrderedDict([("d", 3), ("e", "abc")])
    )

2

Usa% pdef nella riga di comando (IPython), stamperà solo la firma.

per esempio %pdef np.loadtxt

 np.loadtxt(fname, dtype=<class 'float'>, comments='#', delimiter=None, converters=None, skiprows=0, usecols=None, unpack=False, ndmin=0, encoding='bytes')
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.