如何获取Python内置函数的参数数量?

3 投票
5 回答
3274 浏览
提问于 2025-04-16 01:28

我需要通过编程的方式获取一个函数需要多少个参数。对于在模块中声明的函数,这个问题很简单:

myfunc.func_code.co_argcount

但是内置函数没有func_code这个属性。有没有其他方法可以做到这一点?否则我就不能使用内置函数,只能在我的代码里重新写一遍。

[补充] 感谢大家的回复,希望它们能帮到我。我已经改用Pypy了。

5 个回答

1

也许有一个更强大的替代方案来处理函数参数的功能,但这仍然无法准确给出参数的说明,因为并不是所有的文档字符串都能完整地表示它们函数的签名。

举个好例子,dict.get 这个函数,它的参数说明应该是 (k, d=None),但是我定义的函数会返回 (k, d),因为在 d 的形式 d=None 中没有给出默认值。在文档字符串 "f(a[, b, c])" 中,参数 bc 是默认参数,但实际上没有办法直接解析它们,因为没有明确指定值。而在 dict.get 的情况下,行为是在后面描述的,而不是在签名中表示。

不过好的一面是,这个方法可以捕捉到所有的参数,只是默认值不太可靠。

import re
import inspect

def describe_function(function):
    """Return a function's argspec using its docstring

    If usages discovered in the docstring conflict, or default
    values could not be resolved, a generic argspec of *arg
    and **kwargs is returned instead."""
    s = function.__doc__
    if s is not None:
        usages = []
        p = r'([\w\d]*[^\(])\( ?([^\)]*)'
        for func, usage in re.findall(p, s):
            if func == function.__name__:
                usages.append(usage)

        longest = max(usages, key=lambda s: len(s))
        usages.remove(longest)

        for u in usages:
            if u not in longest:
                # the given usages weren't subsets of a larger usage.
                return inspect.ArgSpec([], 'args', 'kwargs', None)
        else:
            args = []
            varargs = None
            keywords = None
            defaults = []

            matchedargs = re.findall(r'( ?[^\[,\]]*) ?,? ?', longest)
            for a in [a for a in matchedargs if len(a)!=0]:
                if '=' in a:
                    name, default = a.split('=')
                    args.append(name)
                    p = re.compile(r"<\w* '(.*)'>")
                    m = p.match(default)
                    try:
                        if m:
                            d = m.groups()[0]
                            # if the default is a class
                            default = import_item(d)
                        else:
                            defaults.append(eval(default))
                    except:
                        # couldn't resolve a default value
                        return inspect.ArgSpec([], 'args', 'kwargs', None)
                elif '**' in a:
                    keywords = a.replace('**', '')
                elif '*' in a:
                    varargs = a.replace('*', '')
                else:
                    args.append(a)
            return inspect.ArgSpec(args, varargs, keywords, defaults)

# taken from traitlet.utils.importstring
def import_item(name):
    """Import and return ``bar`` given the string ``foo.bar``.

    Calling ``bar = import_item("foo.bar")`` is the functional equivalent of
    executing the code ``from foo import bar``.

    Parameters
    ----------
    name : string
      The fully qualified name of the module/package being imported.

    Returns
    -------
    mod : module object
       The module that was imported.
    """
    if not isinstance(name, string_types):
        raise TypeError("import_item accepts strings, not '%s'." % type(name))
    name = cast_bytes_py2(name)
    parts = name.rsplit('.', 1)
    if len(parts) == 2:
        # called with 'foo.bar....'
        package, obj = parts
        module = __import__(package, fromlist=[obj])
        try:
            pak = getattr(module, obj)
        except AttributeError:
            raise ImportError('No module named %s' % obj)
        return pak
    else:
        # called with un-dotted string
        return __import__(parts[0])
1

我觉得用内置函数或者任何C扩展函数是无法做到这种自我检查的。

这里已经有人问过一个类似的问题,Alex的回答建议通过解析函数的文档字符串来确定参数的数量。

2

看看下面这个函数,它是从这里复制过来的。这可能是你能做到的最好效果。注意关于inspect.getargspec的注释。

def describe_builtin(obj):
   """ Describe a builtin function """

   wi('+Built-in Function: %s' % obj.__name__)
   # Built-in functions cannot be inspected by
   # inspect.getargspec. We have to try and parse
   # the __doc__ attribute of the function.
   docstr = obj.__doc__
   args = ''

   if docstr:
      items = docstr.split('\n')
      if items:
         func_descr = items[0]
         s = func_descr.replace(obj.__name__,'')
         idx1 = s.find('(')
         idx2 = s.find(')',idx1)
         if idx1 != -1 and idx2 != -1 and (idx2>idx1+1):
            args = s[idx1+1:idx2]
            wi('\t-Method Arguments:', args)

   if args=='':
      wi('\t-Method Arguments: None')

   print

撰写回答