如何在Python中获取内置函数的args数量?

时间:2022-03-07 04:18:33

I need to programmatically get the number of arguments that a function requires. With functions declared in modules this is trivial:

我需要以编程方式获取函数所需的参数数量。使用模块中声明的函数,这是微不足道的:

myfunc.func_code.co_argcount

But built-in functions don't have the func_code attribute. Is there another way to do this? Otherwise I can't use the built-ins and have to re-write them in my code.

但是内置函数没有func_code属性。还有另一种方法吗?否则我不能使用内置函数,必须在我的代码中重写它们。

[addition] Thanks for the responses, hope they'll be useful. I have used Pypy instead.

[补充]感谢您的回复,希望它们会有用。我用过Pypy代替了。

5 个解决方案

#1


2  

Take a look at the function below copied from here. This may be the best you can do. Note the comments about inspect.getargspec.

看看下面从这里复制的功能。这可能是你能做的最好的事情。请注意有关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

#2


1  

I don't believe this type of introspection is possible with built-in functions, or any C extension function for that matter.

我不相信这种类型的内省是可能的内置函数,或任何C扩展函数。

A similar question was already asked here, and Alex's answer suggests parsing the docstring of the function to determine the number of args.

这里已经提出了类似的问题,Alex的回答建议解析函数的docstring以确定args的数量。

#3


1  

Perhaps a more robust alternative to the parsing function given by Alex, but this will still fail to give appropriate arg specs because, not all docstrings fully represent their function's signature.

也许是Alex给出的解析函数的更强大的替代方法,但是仍然无法给出适当的arg规范,因为并非所有文档字符串都完全代表其函数的签名。

A good example being dict.get where the args spec should be (k, d=None), but the function I've defined will return (k, d), because no default is given for d in the form d=None. In the docstring "f(a[, b, c])" the args b and c are defaults, but there's no real way to parse them because no value is directly specified and, in the case of dict.get, the behavior is described later on, not in the signature representation.

一个很好的例子是dict.get,其中args规范应该是(k,d = None),但是我定义的函数将返回(k,d),因为没有给出d的默认值,形式为d = None。在文档字符串“f(a [,b,c])”中,args b和c是默认值,但没有真正的方法来解析它们,因为没有直接指定值,并且在dict.get的情况下,行为是稍后描述,而不是签名表示。

On the bright side though, this captures all the arguments, it's just that defaults aren't reliable.

但从好的方面来说,它捕获了所有的参数,只是默认值不可靠。

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

#4


0  

It's not possible. C functions do not expose their argument signatures programmatically.

这是不可能的。 C函数不以编程方式公开其参数签名。

#5


0  

Interesting solution, ars. Hope it helps others too.

有趣的解决方案,ars。希望它也能帮助别人。

I went another way: I had heared that Pypy is python implemented mostly in itself. So I tried PyPy (JIT version) and it worked. I haven't found a "hard-coded" function yet. Couldn't find out how to install it in /usr, but it works from a folder where it's unpacked.

我采取了另一种方式:我听说过Pypy本身就是python实现的。所以我尝试了PyPy(JIT版本)并且它有效。我还没有找到“硬编码”功能。无法找到如何在/ usr中安装它,但它可以从解压缩的文件夹中运行。

#1


2  

Take a look at the function below copied from here. This may be the best you can do. Note the comments about inspect.getargspec.

看看下面从这里复制的功能。这可能是你能做的最好的事情。请注意有关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

#2


1  

I don't believe this type of introspection is possible with built-in functions, or any C extension function for that matter.

我不相信这种类型的内省是可能的内置函数,或任何C扩展函数。

A similar question was already asked here, and Alex's answer suggests parsing the docstring of the function to determine the number of args.

这里已经提出了类似的问题,Alex的回答建议解析函数的docstring以确定args的数量。

#3


1  

Perhaps a more robust alternative to the parsing function given by Alex, but this will still fail to give appropriate arg specs because, not all docstrings fully represent their function's signature.

也许是Alex给出的解析函数的更强大的替代方法,但是仍然无法给出适当的arg规范,因为并非所有文档字符串都完全代表其函数的签名。

A good example being dict.get where the args spec should be (k, d=None), but the function I've defined will return (k, d), because no default is given for d in the form d=None. In the docstring "f(a[, b, c])" the args b and c are defaults, but there's no real way to parse them because no value is directly specified and, in the case of dict.get, the behavior is described later on, not in the signature representation.

一个很好的例子是dict.get,其中args规范应该是(k,d = None),但是我定义的函数将返回(k,d),因为没有给出d的默认值,形式为d = None。在文档字符串“f(a [,b,c])”中,args b和c是默认值,但没有真正的方法来解析它们,因为没有直接指定值,并且在dict.get的情况下,行为是稍后描述,而不是签名表示。

On the bright side though, this captures all the arguments, it's just that defaults aren't reliable.

但从好的方面来说,它捕获了所有的参数,只是默认值不可靠。

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

#4


0  

It's not possible. C functions do not expose their argument signatures programmatically.

这是不可能的。 C函数不以编程方式公开其参数签名。

#5


0  

Interesting solution, ars. Hope it helps others too.

有趣的解决方案,ars。希望它也能帮助别人。

I went another way: I had heared that Pypy is python implemented mostly in itself. So I tried PyPy (JIT version) and it worked. I haven't found a "hard-coded" function yet. Couldn't find out how to install it in /usr, but it works from a folder where it's unpacked.

我采取了另一种方式:我听说过Pypy本身就是python实现的。所以我尝试了PyPy(JIT版本)并且它有效。我还没有找到“硬编码”功能。无法找到如何在/ usr中安装它,但它可以从解压缩的文件夹中运行。