Wie kann ich die Signatur einer Funktion einschließlich der Standardargumentwerte lesen?

129

Wie kann ich bei einem Funktionsobjekt seine Signatur erhalten? Zum Beispiel für:

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

Ich würde gerne bekommen "myMethod(firt, second, third='something')".

Spì
quelle
3
Können Sie bitte Ihre spezifische Frage näher erläutern und vielleicht ein Beispiel mit dem erwarteten Ergebnis geben?
Jhwist
Vermutlich sucht er nach Funktionen in Python- oder Drittanbieter-Bibliotheken, die die Signatur einer Methode (Namen und Parametertypen sowie Rückgabewert) unter Angabe des Methodennamens zurückgeben.
Michael Petrotta
1
Unterschrift wie in wie man es nennt und so? Versuchen Sie help(yourmethod)zBhelp(map)
Nick T
Nur Parameter: stackoverflow.com/questions/218616/…
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功

Antworten:

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

Beachten Sie jedoch, dass dies inspect.getargspec()seit Python 3.0 veraltet ist.

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

Python 3.5+ empfiehlt inspect.signature().

unutbu
quelle
AttributeError: 'Modul'-Objekt hat kein Attribut' getargspec '
Spì
3
@Spi, Sie rufen inspect.getargspecein Modul auf, keine Funktion.
Mike Graham
Vielen Dank, das Problem war mit Eclipse, die das Inspektionsmodul nicht gesehen hat
Spì
Wenn eine Funktion nur Argumentanmerkungen oder Argumente mit Schlüsselwörtern enthält (= wenn Sie Python 3 verwenden), müssen Sie getfullargspecstattdessen aufrufen . ( ValueError: Function has keyword-only arguments or annotations, use getfullargspec() API which can support them)
Badp
2
@darth_coder: In Python2, getargspecerhöht , TypeErrorwenn der Eingang als nicht erkannt wird Python - Funktion - das heißt, eine Funktion in Python implementiert. In CPython Exception.__init__ist in C implementiert, daher die TypeError. Sie müssen den Quellcode überprüfen, um die Anrufsignatur zu verstehen. In Python3 getargspecwird anders implementiert und dort wird inspect.getargspec(Exception.__init__)eine ArgSpecInstanz zurückgegeben.
Unutbu
44

Der wahrscheinlich einfachste Weg, die Signatur für eine Funktion zu finden, wäre 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)

Außerdem wurde in Python 3 dem inspectaufgerufenen Modul eine Methode hinzugefügt, die signaturedie Signatur eines aufrufbaren Objekts und seine Rückgabeanmerkung darstellt :

>>> 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'>
Stefan van den Akker
quelle
3
inspect.signatureist auch für Python 2 über das funcsigsBackport-Projekt verfügbar
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")
Arup Malakar
quelle
Gibt es überhaupt eine Erklärung dafür, was dies tun soll?
Grantmcconnaughey
1
Kommentare zum Codebeispiel hinzugefügt, hoffe das hilft.
Arup Malakar
Schönes Zeug. Noch besser wäre , wenn Sie es an die Arbeit einstellen konnte mit def foo(a, *, b:int, **kwargs)callte mitfoo(4, b=3.3)
ofer.sheffer
8

Rufen Sie helpein Objekt auf, um es herauszufinden.

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

append(...)
    L.append(object) -- append object to end
Mike Graham
quelle
7

Vielleicht etwas spät zur Party, aber wenn Sie auch die Reihenfolge der Argumente und ihre Standardeinstellungen beibehalten möchten, können Sie das Abstract Syntax Tree-Modul (ast) verwenden .

Hier ist ein Proof of Concept (Vorsicht vor dem Code, um die Argumente zu sortieren und sie ihren Standardeinstellungen zuzuordnen, kann definitiv verbessert / klarer gemacht werden):

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
Jir
quelle
Beachten Sie, dass nicht standardmäßige Argumente nicht auf Standardargumente folgen können , sodass wir sie einfach vom Ende her abgleichen können.
Evgeni Sergeev
5

Wenn Sie nur versuchen, die Funktion zu drucken, verwenden Sie 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)

Wenn Sie versuchen, die Funktionssignatur tatsächlich zu analysieren, verwenden Sie argspec des Inspektionsmoduls. Ich musste das tun, wenn ich die Hook-Skript-Funktion eines Benutzers in einem allgemeinen Framework validierte.

Al Conrad
quelle
3

Beispielcode:

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")])
    )
Weaming
quelle
2

Verwenden Sie% pdef in der Befehlszeile (IPython), es wird nur die Signatur gedruckt.

z.B %pdef np.loadtxt

 np.loadtxt(fname, dtype=<class 'float'>, comments='#', delimiter=None, converters=None, skiprows=0, usecols=None, unpack=False, ndmin=0, encoding='bytes')
Liyuan
quelle