与Python函数调用匹配的正则表达式

问题描述:

我想在Python中创建一个正则表达式,它将匹配Python源代码中的一行并返回函数调用列表。与Python函数调用匹配的正则表达式

典型的线路是这样的:

something = a.b.method(time.time(), var=1) + q.y(x.m()) 

,其结果应该是:

["a.b.method()", "time.time()", "q.y()", "x.m()"] 

我这里有两个问题:

  1. 创建正确的模式
  2. 捕获组重叠

谢谢大家帮忙

+0

那么解析字符串和注释怎么样? – Qtax 2011-12-28 16:35:51

+2

python不是一种常规的语言,所以你不能用正则表达式。 – 2011-12-28 16:43:05

+0

@DouglasLeeder,正则表达式不正规。除非我们在这里讨论形式语言理论。 ;-) – Qtax 2011-12-28 16:46:13

我不认为正则表达式是最好的方法。考虑ast module代替,例如:

class ParseCall(ast.NodeVisitor): 
    def __init__(self): 
     self.ls = [] 
    def visit_Attribute(self, node): 
     ast.NodeVisitor.generic_visit(self, node) 
     self.ls.append(node.attr) 
    def visit_Name(self, node): 
     self.ls.append(node.id) 


class FindFuncs(ast.NodeVisitor): 
    def visit_Call(self, node): 
     p = ParseCall() 
     p.visit(node.func) 
     print ".".join(p.ls) 
     ast.NodeVisitor.generic_visit(self, node) 


code = 'something = a.b.method(foo() + xtime.time(), var=1) + q.y(x.m())' 
tree = ast.parse(code) 
FindFuncs().visit(tree) 

结果

a.b.method 
foo 
xtime.time 
q.y 
x.m 
+0

“ast”模块上的+1教程!很高兴知道它提供的东西比'literal_eval'更有用:) :) – 2011-12-28 19:41:33

+0

事实上,除非我错了,否则基于正则表达式的方法注定要失败。 Python语言基于上下文无关语法,并且(除非我错了),CFG比正则表达式更具表现力(谢谢[Chomsky Hierarchy](http://en.wikipedia.org/wiki/) Chomsky_hierarchy) – 2012-09-10 17:22:59

+2

@AdamParkin:[这个问题]的一些答案(http://*.com/questions/11306641/what-kind-of-formal-languages-can-modern-regex-engines-parse)可能是有趣的你 – georg 2012-09-10 18:00:22

/([.a-zA-Z]+)\(/g 

应该匹配方法名;因为你有一些嵌套后你必须添加parens。

+0

'foo(“bar(a,b)”)'会为该正则表达式错误地返回'bar'。 – 2011-12-28 16:42:37

+0

@DouglasLeeder公平点。 – Mathletics 2011-12-28 16:49:17

+0

@DouglasLeeder它看起来不错,但[this](http://pastebin.com/7dKpRh5B)Python代码不能打印预期的内容。 – xralf 2011-12-28 17:35:51

我真的不知道Python,但我可以想像,使这项工作正常涉及一些并发症,如:

  • 评论
  • 表达式返回一个对象的

但是对于您的示例,像这样的表达式有效:

(?:\w+\.)+\w+\(

$ python3 
>>> import re 
>>> from itertools import chain 
>>> def fun(s, r): 
...  t = re.sub(r'\([^()]+\)', '()', s) 
...  m = re.findall(r'[\w.]+\(\)', t) 
...  t = re.sub(r'[\w.]+\(\)', '', t) 
...  if m==r: 
...   return 
...  for i in chain(m, fun(t, m)): 
...   yield i 
... 
>>> list(fun('something = a.b.method(time.time(), var=1) + q.y(x.m())', [])) 
['time.time()', 'x.m()', 'a.b.method()', 'q.y()'] 


我为你证明这一点的例子是在Python3可行

 
    import re 


    def parse_func_with_params(inp): 
     func_params_limiter = "," 
     func_current_param = func_params_adder = "\s*([a-z-A-Z]+)\s*" 

     try: 
      func_name = "([a-z-A-Z]+)\s*" 
      p = re.compile(func_name + "\(" + func_current_param + "\)") 
      print(p.match(inp).groups()) 
     except: 
      while 1: 
       func_current_param += func_params_limiter + func_params_adder 
       try: 
        func_name = "([a-z-A-Z]+)\s*" 
        p = re.compile(func_name + "\(" + func_current_param + "\)") 
        print(p.match(inp).groups()) 
        break 
       except: 
        pass 

命令线路输入put:animalFunc
输出:('animalFunc','lion','tiger','giraffe','singe')

正如你所看到的,函数名永远是第一个在列表中,其余的是通过的参数名称