Hoe kan ik de handtekening van een functie lezen, inclusief standaardargumentwaarden?

Gegeven een functie-object, hoe kan ik de handtekening ervan krijgen? Bijvoorbeeld voor:

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

Ik wil graag "myMethod(firt, second, third='something')"ontvangen.


Antwoord 1, autoriteit 100%

import inspect
def foo(a, b, x='blah'):
    pass
print(inspect.signature(foo))
# (a, b, x='blah')

Python 3.5+ beveelt inspect.signature().


Antwoord 2, autoriteit 24%

De gemakkelijkste manier om de handtekening voor een functie te vinden, is waarschijnlijk 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)

Ook in Python 3 is een methode toegevoegd aan de module inspectgenaamd signature, die is ontworpen om de handtekening van een aanroepbaar object en de bijbehorende annotatie:

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

Antwoord 3, autoriteit 7%

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

Antwoord 4, autoriteit 4%

Probeer helpte bellen op een object om er meer over te weten te komen.

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

Antwoord 5, autoriteit 4%

Misschien een beetje laat voor het feest, maar als je ook de volgorde van de argumenten en hun standaardwaardenwilt behouden, dan kun je de Abstracte syntaxisstructuurmodule (ast).

Hier is een proof of concept (pas op dat de code om de argumenten te sorteren en ze af te stemmen op hun standaardwaarden kan zeker worden verbeterd/verduidelijkt):

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

Antwoord 6, autoriteit 3%

Als je alleen de functie probeert af te drukken, gebruik dan 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)

Als je de functiehandtekening daadwerkelijk probeert te analyseren, gebruik dan argspec van de inspectiemodule. Ik moest dat doen bij het valideren van de hook-scriptfunctie van een gebruiker in een algemeen raamwerk.


Antwoord 7, autoriteit 3%

Gebruik %pdef in de opdrachtregel (IPython), het zal alleen de handtekening afdrukken.

bijv. %pdef np.loadtxt

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

Antwoord 8, autoriteit 3%

Voorbeeldcode:

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")])
    )

Other episodes