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.
精彩评论