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中安装它,但它可以从解压缩的文件夹中运行。