解析Python函数声明

Tho*_*ski 2 python regex

为了为我的 Python 代码编写自定义文档生成器,我想编写一个能够匹配以下内容的正则表达式:

def my_function(arg1,arg2,arg3):
"""
DOC
"""
Run Code Online (Sandbox Code Playgroud)

我当前的问题是,使用以下正则表达式:

def (.+)\((?:(\w+),)*(\w+)\)
Run Code Online (Sandbox Code Playgroud)

我只能匹配my_function, arg2and arg3(根据 Pythex.org)。

我不明白我做错了什么,因为我(?:(\w+),)*应该匹配尽可能多的参数,直到最后一个(此处arg3)。有人能解释一下吗?

谢谢

sna*_*ard 5

这在一般意义上是不可能的,因为 Python 函数不是正则表达式——它们可以采用正则表达式语法无法捕获的形式,特别是在其他 Python 结构的上下文中。但请放心,从你的问题中可以学到很多东西!

令人着迷的是,尽管您说您正在尝试学习正则表达式,但您却意外地陷入了计算机科学本身的核心,即编译器理论!

我将在这篇文章中介绍不到冰山一角的一小部分,以帮助您入门,然后建议一些免费和付费资源来帮助您继续。

python 函数本身可以采用多种形式:

def foo(x):
    "docstring"
    <body>

def foo1(x):
    """doc
    string"""
    <body>

def foo2(x):
    <body>
Run Code Online (Sandbox Code Playgroud)

另外,之前之后发生的事情内容可能不是另一个函数!

这就是使用正则表达式本身不可能自动生成文档的原因(好吧,对我来说不可能。我不够聪明,无法编写一个可以解释整个 Python 语言的正则表达式!)。

您需要研究的是解析(顺便说一句,我非常宽松地使用术语解析 来涵盖解析、标记化和词法分析,只是为了让事情保持“简单”)使用正则表达式通常是解析的一个非常重要的部分。

一般策略是将文件解析为语法结构。确定哪些构造是函数。隔离该函数的文本。然后您可以使用正则表达式来解析该构造的文本。或者,您可以进一步解析一个级别,并将函数分解为不同的语法结构——函数名称、参数声明、文档字符串、正文等……此时您的问题将得到解决。

我试图为标准函数定义(不解析)编写正则表达式,例如fooor foo1,但即使编写了几种语言,我也很难这样做。

因此,需要明确的是,与简单的正则表达式相反,我会考虑解析的一点是当您的输入跨越多行时。正则表达式对于单行 最有效。

解析函数如下所示:

def parse_fn_definition(definition):

    def parse_name(lines):
       <code>

    def parse_args(lines):
       <code>

    def parse_doc(lines):
       <code>

    def parse_body(lines):
       <code>
    ...
Run Code Online (Sandbox Code Playgroud)

现在真正的技巧是:每个parse函数都返回两个内容:

0) 解析的正则表达式块 1) 行的其余部分

例如,

def parse_name(lines):
    pattern = 'def\s*?([a-zA-Z_][a-zA-Z0-9_]*?)'
    for line in lines:
        m = re.match(pattern, line)
        if m:
            res, rest = m.groups()
            return res, [rest] + lines
        else:
            raise Exception("Line cannot be parsed by parse_name: {}".format(line))
Run Code Online (Sandbox Code Playgroud)

因此,一旦你隔离了函数文本(这是一套完整的其他技巧,通常涉及创建称为“语法”的东西 - 别担心,我在下面为你设置了一些资源),你可以使用以下技术解析函数文本:

def parse_fn(lines_of_text):
    name, rest = parse_name(lines_of_text)
    params, rest = parse_params(rest)
    doc_string, rest = parse_doc(rest)
    body, rest = parse_body(rest)
    function = [name, params, doc_string, body]
    res = function, rest
    return res
Run Code Online (Sandbox Code Playgroud)

该函数将返回一些表示该函数的数据结构(我只是使用了一个简单的list示例)和其余文本行。这将被传递到适当地对您的function数据进行分类,然后对rest文本进行分类和处理的东西!

无论如何,如果这是您感兴趣的事情,请不要放弃!我想提供一些粗浅的建议:

1)从更容易解析的语言开始,例如Scheme/LISP。这些语言被设计为易于解析和操作!然后逐步学习更不规​​则的语言。

2a) Peter Norvig 在这方面做了一些令人惊奇且非常容易理解的工作。看看Lispy

2b) Peter Norvig 的CS212类(具体为第 3 单元代码)非常具有挑战性,但在介绍基本语言设计概念方面做得非常出色。我得到的每一份工作以及我对编程的热爱都是因为那门课程。

3) 如果您想进一步提升自己并且负担得起,我强烈建议您查看 Dave Beazely 的编译器解释器研讨会。我已经从戴夫那里学习了两门课程,虽然我不能向每个人保证这一点,但每门课程后我的工资实际上翻了一番,所以我认为这是一项值得的投资。

4)一定要看看《计算机程序的结构和解释》(向导书)和《编译器》(龙书)。他们会改变你的生活。

5)不要放弃!你得到了这个!祝你好运!

  • @snakes_on_a_keyboard 优秀的软件工程师和优秀的软件工程师之间的区别在于优秀的软件工程师比较懒。不幸的是,糟糕的软件工程师和糟糕的软件工程师之间的区别在于糟糕的软件工程师更懒。这都是关于以正确的方式偷懒...... (2认同)