【问题标题】:How can I read a function's signature including default argument values?如何读取包含默认参数值的函数签名?
【发布时间】:2011-02-10 06:07:31
【问题描述】:

给定一个函数对象,我怎样才能得到它的签名?例如,对于:

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

我想得到"myMethod(firt, second, third='something')"

【问题讨论】:

  • 您能否详细说明您的具体问题,并举例说明预期结果?
  • 可能他正在寻找 Python 或第三方库中的功能,这些功能将在给定方法名称的情况下返回方法的签名(参数的名称和类型以及返回值)。
  • 签名如何称呼它等等?试试help(yourmethod) 例如help(map)

标签: python arguments inspect


【解决方案1】:

尝试在一个对象上调用help 以了解它。

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

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

【讨论】:

    【解决方案2】:
    import inspect
    
    def foo(a, b, x='blah'):
        pass
    
    print(inspect.signature(foo))
    # ArgSpec(args=['a', 'b', 'x'], varargs=None, keywords=None, defaults=('blah',))
    

    Python 3.5+ 推荐inspect.signature()

    【讨论】:

    • AttributeError: 'module' 对象没有属性 'getargspec'
    • @Spi,你在一个模块上调用inspect.getargspec,而不是一个函数。
    • 谢谢,问题出在 Eclipse 上,没有看到检查模块
    • 如果函数有参数注释或仅关键字参数(= 如果您使用 Python 3),则必须改为调用 getfullargspec。 (ValueError: Function has keyword-only arguments or annotations, use getfullargspec() API which can support them)
    • @darth_coder:在 Python2 中,如果输入未被识别为 Python 函数,getargspec 将引发 TypeError ——即,在 Python 中实现的函数。在 CPython 中,Exception.__init__ 是在 C 中实现的,因此是 TypeError。您必须检查源代码才能理解调用签名。在 Python3 中,getargspec 的实现方式不同,inspect.getargspec(Exception.__init__) 返回一个 ArgSpec 实例。
    【解决方案3】:
    #! /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")
    

    【讨论】:

    • 对这应该做什么有任何解释吗?
    • 在代码示例中添加了 cmets,希望对您有所帮助。
    • 可爱的东西。如果您可以调整它以使用 def foo(a, *, b:int, **kwargs) 调用 foo(4, b=3.3) 会更好
    【解决方案4】:

    可以说找到函数签名的最简单方法是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)
    

    此外,在 Python 3 中,inspect 模块中添加了一个名为 signature 的方法,该方法旨在表示 signature of a callable object and its return annotation

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

    【讨论】:

    • inspect.signature 也可通过funcsigs backport 项目用于 Python 2:pypi.python.org/pypi/funcsigs
    • inspect.signaturetyping.get_type_hints有什么区别?
    【解决方案5】:

    可能有点晚了,但如果你还想保持参数的顺序和它们的默认值,那么你可以使用Abstract Syntax Tree module (ast)

    这是一个概念证明(注意对参数进行排序并将它们与默认值匹配的代码肯定可以改进/更清晰):

    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
    

    【讨论】:

    【解决方案6】:

    如果您只想打印函数,请使用 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)
    

    如果您要实际分析函数签名,请使用检查模块的 argspec。在将用户的钩子脚本函数验证到通用框架中时,我必须这样做。

    【讨论】:

      【解决方案7】:

      在命令行中使用 %pdef (IPython),它只会打印签名。

      例如%pdef np.loadtxt

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

      【讨论】:

        【解决方案8】:

        示例代码:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-02-20
          • 2011-04-09
          • 1970-01-01
          • 1970-01-01
          • 2017-09-18
          相关资源
          最近更新 更多