【问题标题】:How to use regex to find functions call themself?如何使用正则表达式查找函数调用自己?
【发布时间】:2012-02-02 00:55:29
【问题描述】:

我有一个code.py

def funA():
    print('A')
    funA()

def funB():
    print('B')

def funC():
    print('C')
    funB()
    funC()

我想找到所有函数自己调用:

funA
funC

regex怎么写?

约束:

  • 所有函数调用正常:funname(arg1, arg2, ...)
  • 无混淆方式(如lambda,exec
  • 无间接递归

【问题讨论】:

  • 为什么要编写正则表达式而不是遍历 AST?
  • 由于 Python 的语法是上下文无关的,因此仅使用正则表达式是不可能找到所有递归函数的。
  • 您真的还坚持为此使用正则表达式吗?我想我们已经很清楚地表明这是不可能的。
  • 像这样的工作是解析器做的。而且您已经在 Python 中内置了一个用于 Python 的解析器,因此您甚至不必自己编写解析器,只需阅读一些关于如何在获得 AST 后遍历它的模块文档。使用正则表达式,这是一项非常困难的任务(并且可以证明只能大致完成),但使用解析器非常容易。所以使用解析器!

标签: python regex shell awk


【解决方案1】:

这很难,因为函数可以以混淆的方式调用自己。例如,这算不算?

def funA():
  print 'A'
  foo = funA
  foo()

funA()

这个怎么样?

def funA():
  funB()

def funB():
  funA()

funA()

甚至这个?

def funA():
  exec('Anuf'[::-1] + '()')

funA()

我认为你不能用正则表达式来做到这一点。


即使考虑到您的新编辑,即使不是不可能,它仍然会非常困难。以这个函数为例。

def funA():
  if 1 + 1 == 2:
    return
  funA()

我建议你听从 Ignacio Vazquez-Abrams 的建议,看看ast

【讨论】:

  • 事实上,你不能用正则表达式来做到这一点。
  • 我也怀疑过,但我不喜欢“断言证明”:)
  • 很容易证明Python不是一门常规语言。
  • “恐吓证明”也好不到哪里去 :)
  • 非正则语言中格式良好的括号中的每个单词都是有效的 Python 程序。每个由() 组合但不是该语言的单词都不是有效的Python 程序。因此,仅由括号组成的 Python 子集是非常规的。因此 Python 是一种非常规语言。
【解决方案2】:

是的,我相信正则表达式不可能匹配 wim 指出的 self 调用被混淆的情况。但是,这里有一个正则表达式,它可以为简单的自我调用(形式为funcname(...))做一个半体面的工作。此正则表达式正确匹配原始问题中列出的所有测试用例:

reobj = re.compile(r"""
    # Match (unreliably) Python function with self reference.
    ^                        # Anchor to start of line.
    ([ \t]*)                 # $1: Indentation of DEF statement.
    def[ \t]+                # Function definition.
    ([^\s(]+)                # $2: Name of function to find.
    .*\r?\n                  # Remainder of function def line.
    (?:                      # Zero or more lines w/o funcname.
      (?:                    # Function block lines alternatives.
        \1[ \t]+             # Func block lines have extra indentation.
        (?:(?!\b\2\s*\().)*  # Optional non-funcname stuff on line
      | [ \t]*\#.*           # Allow comment lines to defy indent rules.
      )?                     # Allow blank lines in function block.
      \r?\n                  # End of line not containing funcname.
    )*                       # Zero or more lines w/o funcname
    \1[ \t]+                 # Now match the line having funcname.
    (?:(?!\b\2\s*\().)*      # Optional non-funcname stuff on line
    \b\2\s*\(                # Match the function self reference.
    """, re.MULTILINE | re.VERBOSE)

它从函数定义行开始匹配,并捕获$1 组中'def' 之前的空白缩进和$2 组中的函数名称。然后,它匹配函数块中不包含函数名称的行,每行都比函数定义具有更多的前导空格。它跳过空行和仅包含 cmets 的行。一旦它在函数块中找到一个函数名后跟左括号的行,它就声明一个匹配,表示对自身的调用。否则,它声明一个不匹配,然后继续寻找下一个可能的匹配。

请注意,此解决方案不可靠,如果函数名称出现在字符串内或一行中其他代码之后的注释内,则会导致误报。它也不处理具有多行原始字符串的函数。但是,它会正确捕获不少!

【讨论】:

  • 这就像给一个人除草机来修剪他的鼻毛。
  • 不完全是。这个答案只是回答了所提出的具体问题,并附有关于其(非常真实的)局限性的免责声明。对于一次性搜索源文件,这个答案肯定会很有用。只是好奇, ast 模块在查找您引用的特殊情况方面会更好吗?这个解决方案有多难?为什么不发布呢? IMO 唯一 100% 准确的解决方案是彻底分析/调试运行所有可能路径的应用程序。但是,是的,这个解决方案绝对是一个除草剂! (这是要求的。)
【解决方案3】:

简单地说,您将无法使用正则表达式来做到这一点。您至少需要解析函数定义,保持某种关于您当前正在解析的函数的状态,并在当前函数的范围内搜索当前函数名称的调用。

【讨论】:

    【解决方案4】:

    我将描述我认为您想要的模式:以 def 开头的行,后跟空格,然后是名称(您在括号中捕获),然后是一组(可能为空)开头的行带有空格,后跟一行以空格开头并包含您的函数名称,后跟一个开放的括号(以便您捕获实际调用,而不仅仅是引用)。

    【讨论】:

    • 非空——在 Python 中,您必须使用 pass 语句而不是空主体。
    • @PlatinumAzure def funA(): funA() 是一个调用自身的有效 Python 函数。如果紧随其后的是另一个全局范围语句,则在 def 之后不会有以空格开头的行。然而,斯科特对正则表达式的描述也与此不符。
    【解决方案5】:

    也许你可以使用gawk,下面是我的示例代码,你可能需要修改它:

    #! /usr/bin/gawk -f
    {
        currentLine = $0
        if (currentLine ~ /def/){
            inFunction = "true"
            nameIndex = index($2,"(")
            functionName = substr($2,1,nameIndex - 1)
            #print functionName
            next
        }
        if (inFunction == "true" && currentLine ~ functionName){
           inFunction = false 
           print "recursive function is: " functionName
        }
    }
    

    只要运行程序,你就会得到你想要的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-25
      • 1970-01-01
      • 2021-06-16
      • 1970-01-01
      相关资源
      最近更新 更多