【问题标题】:How to get the number of args of a built-in function in Python?如何在 Python 中获取内置函数的 args 数量?
【发布时间】:2010-07-18 18:08:21
【问题描述】:

我需要以编程方式获取函数所需的参数数量。对于在模块中声明的函数,这很简单:

myfunc.func_code.co_argcount

但是内置函数没有func_code 属性。还有另一种方法可以做到这一点吗?否则我无法使用内置函数,必须在我的代码中重新编写它们。

[补充] 感谢您的回复,希望它们对您有用。我改用 Pypy。

【问题讨论】:

标签: python


【解决方案1】:

看看下面从here复制的函数。这可能是你能做的最好的。注意关于inspect.getargspec的cmets。

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】:

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

    这里已经问过similar question,Alex 的回答建议解析函数的文档字符串以确定 args 的数量。

    【讨论】:

      【解决方案3】:

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

      一个很好的例子是dict.get,其中args规范应该是(k, d=None),但是我定义的函数将返回(k, d),因为d没有以d=None的形式给出默认值。在文档字符串 "f(a[, b, c])" 中,args 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])
      

      【讨论】:

        【解决方案4】:

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

        【讨论】:

        【解决方案5】:

        有趣的解决方案,ars。希望对其他人也有帮助。

        我走了另一条路:我听说 Pypy 主要是由 Python 实现的。所以我尝试了PyPy(JIT 版本),它成功了。我还没有找到“硬编码”功能。无法找到如何在 /usr 中安装它,但它可以在解压后的文件夹中运行。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-01-06
          • 2018-07-12
          • 2013-12-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多