Bagaimana saya bisa membaca tanda tangan fungsi termasuk nilai argumen default?

129

Diberikan objek fungsi, bagaimana saya bisa mendapatkan tanda tangannya? Misalnya, untuk:

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

Saya ingin mendapatkannya "myMethod(firt, second, third='something')".

Spì
sumber
3
Bisakah Anda menguraikan pertanyaan spesifik Anda dan mungkin memberikan contoh dengan hasil yang diharapkan?
jhwist
Agaknya dia mencari fungsionalitas di Python atau pustaka pihak ketiga yang akan mengembalikan tanda tangan metode (nama dan jenis parameter dan nilai pengembalian) diberi nama metode.
Michael Petrotta
1
Tanda tangan seperti bagaimana menyebutnya dan semacamnya? Coba help(yourmethod)misalnyahelp(map)
Nick T
Parameter Just: stackoverflow.com/questions/218616/…
Ciro Santilli 郝海东 冠状 病 六四 六四 事件 法轮功

Jawaban:

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

Namun, perhatikan bahwa inspect.getargspec()sudah usang sejak Python 3.0.

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

Python 3.5+ merekomendasikan inspect.signature().

unutbu
sumber
AttributeError: objek 'module' tidak memiliki atribut 'getargspec'
Spì
3
@Spi, Anda memanggil inspect.getargspecmodul, bukan fungsi.
Mike Graham
Terima kasih, masalahnya adalah dengan Eclipse yang tidak melihat modul inspeksi
Spì
Jika suatu fungsi memiliki argumen anotasi atau argumen hanya kata kunci (= jika Anda menggunakan Python 3) Anda harus memanggil getfullargspecsaja. ( ValueError: Function has keyword-only arguments or annotations, use getfullargspec() API which can support them)
badp
2
@darth_coder: Dalam Python2, getargspecmemunculkan TypeErrorjika input tidak dikenali sebagai fungsi Python - yaitu, fungsi yang diimplementasikan dalam Python. Dalam CPython, Exception.__init__diimplementasikan dalam C, karenanya TypeError. Anda harus memeriksa kode sumber untuk memahami tanda tangan panggilan. Dalam Python3, getargspecdiimplementasikan secara berbeda, dan ada inspect.getargspec(Exception.__init__)mengembalikan sebuah ArgSpecinstance.
unutbu
44

Mungkin cara termudah untuk menemukan tanda tangan untuk suatu fungsi adalah 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)

Juga, dalam Python 3 metode ditambahkan ke inspectmodul yang disebutsignature , yang dirancang untuk mewakili tanda tangan objek yang dapat dipanggil dan penjelasan kembali :

>>> 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
sumber
3
inspect.signaturejuga tersedia untuk Python 2 melalui funcsigsproyek 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")
Arup Malakar
sumber
Adakah penjelasan sama sekali tentang apa yang seharusnya dilakukan?
grantmcconnaughey
1
Menambahkan komentar ke contoh kode, harap itu membantu.
Arup Malakar
Hal yang indah. Akan lebih baik jika Anda dapat menyesuaikannya agar berfungsi dengan def foo(a, *, b:int, **kwargs)dipanggil denganfoo(4, b=3.3)
ofer.sheffer
8

Coba panggil helpobjek untuk mengetahuinya.

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

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

Mungkin agak terlambat ke pesta, tetapi jika Anda juga ingin menjaga urutan argumen dan default-nya , maka Anda dapat menggunakan modul Abstract Syntax Tree (ast) .

Berikut ini adalah bukti konsep (waspadalah kode untuk mengurutkan argumen dan mencocokkannya dengan standarnya pasti dapat ditingkatkan / dibuat lebih jelas):

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
sumber
Perhatikan bahwa argumen non-default tidak dapat mengikuti argumen default , jadi kita bisa mencocokkannya dari bagian ekor?
Evgeni Sergeev
5

Jika semua yang Anda coba lakukan adalah mencetak fungsinya maka gunakan 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)

Jika Anda benar-benar mencoba menganalisis fungsi tanda tangan maka gunakan argspec dari modul inspeksi. Saya harus melakukan itu ketika memvalidasi fungsi skrip kait pengguna ke dalam kerangka umum.

Al Conrad
sumber
3

Kode contoh:

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")])
    )
menangis
sumber
2

Gunakan% pdef di baris perintah (IPython), itu hanya akan mencetak tanda tangan.

misalnya %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
sumber