2010-07-18 61 views
3

我需要以編程方式獲取函數需要的參數的數量。在模塊中聲明的函數,這是微不足道的:如何獲取Python中內置函數的參數個數?

myfunc.func_code.co_argcount 

但是內置功能沒有func_code屬性。有沒有另一種方法來做到這一點?否則,我不能使用內置插件,而必須在我的代碼中重新編寫它們。

[添加]感謝您的回覆,希望他們會有用。我用Pypy來代替。

+0

我張貼在這裏的另一種方法:https://stackoverflow.com/questions/48567935/get-parameter-count-of-builtin-functions-in-python – HelloWorld 2018-02-01 18:28:14

回答

2

看看下面從here複製的功能。這可能是你能做的最好的。請注意有關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 
1

我不相信這種類型的內省是可能的內置函數或任何C擴展功能。

A similar question已經在這裏提出,Alex的答案建議解析函數的文檔字符串以確定參數的數量。

0

這是不可能的。 C函數不會以編程方式公開它們的參數簽名。

+0

這是可能的。我在這裏發佈了一個解決方案:https://stackoverflow.com/questions/48567935/get-parameterarg-count-of-builtin-functions-in-python – HelloWorld 2018-02-02 04:39:59

0

有趣的解決方案,希望它也能幫助別人。

我採取了另一種方式:我聽說Pypy是python主要實現的。所以我嘗試PyPy(JIT版本),它的工作。我還沒有找到「硬編碼」功能。無法找到如何將其安裝在/ usr中,但它從解壓縮的文件夾中運行。

1

也許是Alex給出的解析函數的一個更強大的替代方法,但是這仍然無法給出適當的arg規範,因爲並不是所有的文檔都完全代表它們函數的簽名。

一個很好的例子是dict.get在ARGS規範應該是(k, d=None),但我已經定義的函數將返回(k, d),因爲沒有默認的形式d=None給予d。在文檔字符串"f(a[, b, c])"中,參數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])