Comment puis-je lire la signature d'une fonction, y compris les valeurs d'argument par défaut?


129

Étant donné un objet fonction, comment puis-je obtenir sa signature? Par exemple, pour:

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

J'aimerais avoir "myMethod(firt, second, third='something')".


3
Pouvez-vous s'il vous plaît élaborer sur votre question spécifique et peut-être donner un exemple avec le résultat attendu?
jhwist

Vraisemblablement, il recherche des fonctionnalités en Python ou dans des bibliothèques tierces qui renverront la signature d'une méthode (noms et types de paramètres et valeur de retour) étant donné le nom de la méthode.
Michael Petrotta

1
Signature comme dans comment l'appeler et tel? Essayez help(yourmethod)par exemplehelp(map)
Nick T

Réponses:


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

Cependant, notez que inspect.getargspec()c'est obsolète depuis Python 3.0.

Python 3.0 à 3.4 recommande inspect.getfullargspec().

Python 3.5+ recommande inspect.signature().


AttributeError: l'objet 'module' n'a pas d'attribut 'getargspec'
Spì

3
@Spi, vous appelez inspect.getargspecun module, pas une fonction.
Mike Graham

Merci, le problème était avec Eclipse qui n'a pas vu le module d'inspection
Spì

Si une fonction a des annotations d'argument ou des arguments de mot-clé uniquement (= si vous utilisez Python 3), vous devez appeler à la getfullargspecplace. ( ValueError: Function has keyword-only arguments or annotations, use getfullargspec() API which can support them)
badp

2
@darth_coder: En Python2, getargspecdéclenche TypeErrorsi l'entrée n'est pas reconnue comme une fonction Python - c'est-à-dire une fonction implémentée en Python. En CPython, Exception.__init__est implémenté en C, d'où le TypeError. Vous devrez vérifier le code source pour comprendre la signature d'appel. En Python3, getargspecest implémenté différemment et inspect.getargspec(Exception.__init__)renvoie une ArgSpecinstance.
unutbu

44

Le moyen le plus simple de trouver la signature d'une fonction serait sans doute 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)

De plus, dans Python 3, une méthode a été ajoutée au inspectmodule appelé signature, qui est conçue pour représenter la signature d'un objet appelable et son annotation de retour :

>>> 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.signatureest également disponible pour Python 2 via le funcsigsprojet 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")

Une explication sur ce que cela est censé faire?
grantmcconnaughey

1
Ajout de commentaires à l'exemple de code, j'espère que cela vous aidera.
Arup Malakar

De belles choses. Ce serait encore mieux si vous pouviez l'ajuster pour fonctionner avec def foo(a, *, b:int, **kwargs)appelé avecfoo(4, b=3.3)
ofer.sheffer

8

Essayez d'appeler helpun objet pour le découvrir.

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

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

7

Peut-être un peu tard pour la partie, mais si vous souhaitez également conserver l'ordre des arguments et leurs valeurs par défaut , vous pouvez utiliser le module Abstract Syntax Tree (ast) .

Voici une preuve de concept (méfiez-vous du code pour trier les arguments et les faire correspondre à leurs valeurs par défaut peut certainement être amélioré / rendu plus clair):

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

Notez que les arguments non par défaut ne peuvent pas suivre les arguments par défaut , nous pouvons donc simplement les faire correspondre à partir de la queue?
Evgeni Sergeev

5

Si tout ce que vous essayez de faire est d'imprimer la fonction, utilisez 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 vous essayez d'analyser réellement la signature de la fonction, utilisez argspec du module d'inspection. Je devais le faire lors de la validation de la fonction de script hook d'un utilisateur dans un cadre général.


3

Exemple de code:

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

Utilisez% pdef dans la ligne de commande (IPython), il n'imprimera que la signature.

par exemple %pdef np.loadtxt

 np.loadtxt(fname, dtype=<class 'float'>, comments='#', delimiter=None, converters=None, skiprows=0, usecols=None, unpack=False, ndmin=0, encoding='bytes')
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.