开发者

How to get the number of args of a built-in function in Python?

开发者 https://www.devze.com 2023-01-07 15:12 出处:网络
I need to programmatically get the number of arguments that a function requires. With functions declared in modules this is trivial:

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.

[addition] Thanks for the responses, h开发者_运维技巧ope they'll be useful. I have used Pypy instead.


Take a look at the function below copied from here. This may be the best you can do. Note the comments about 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


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

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


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.

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.

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


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


Interesting solution, ars. Hope it helps others too.

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消