将字符串解析为 Python 参数列表

flu*_*ffy 5 python parsing

概括

我想将表示 Python 参数列表的字符串解析为可以转发给函数调用的形式。

详细版

我正在构建一个应用程序,我希望能够从文本字符串中解析出参数列表,然后将其转换为*args,**kwargs模式以转发到实际方法。例如,如果我的文本字符串是:

"hello",42,helper="Larry, the \"wise\""
Run Code Online (Sandbox Code Playgroud)

解析的结果将类似于:

args=['hello',42]
kwargs={'helper': 'Larry, the "wise"'}
Run Code Online (Sandbox Code Playgroud)

我知道 Python 的ast模块,但它似乎只提供了一种解析整个语句的机制。我可以通过在它周围发表声明来伪造它,例如

ast.parse('f("hello",42,helper="Larry, the \"wise\"")'
Run Code Online (Sandbox Code Playgroud)

然后将相关字段从Call节点中拉出,但这似乎是一个非常多的迂回工作。

有没有办法从 Python AST 中只解析一种已知的节点类型,或者是否有更简单的方法来获得此功能?

如果有帮助,我只需要能够支持数字和字符串参数,尽管字符串需要支持嵌入式逗号和转义引号等。

如果存在用于在 Python 中构建词法分析器和解析器的现有模块,我也可以定义自己的 AST,但显然我更愿意只使用已经存在且经过正确测试等的功能。

注意:很多答案都集中在如何存储解析结果上,但这不是我关心的;这是我试图解决的解析本身,理想情况下不需要自己编写整个解析器引擎。

此外,我的应用程序已经在使用Jinja,它在自己的模板解析器中有一个 Python-ish 表达式的解析器,尽管我不清楚如何使用它来解析这样的一个子表达式。(不幸的是,这不是进入模板的东西,而是进入自定义 Markdown 过滤器,我希望语法尽可能匹配其匹配的 Jinja 模板函数。)

Ara*_*Fey 8

我认为ast.parse是你最好的选择。

如果参数用空格分隔,我们可以使用shlex.split

>>> shlex.split(r'"hello" 42 helper="Larry, the \"wise\""')
['hello', '42', 'helper=Larry, the "wise"']
Run Code Online (Sandbox Code Playgroud)

但不幸的是,这不会以逗号分隔:

>>> shlex.split(r'"hello",42,helper="Larry, the \"wise\""')
['hello,42,helper=Larry, the "wise"']
Run Code Online (Sandbox Code Playgroud)

我也考虑过使用ast.literal_eval,但这不支持关键字参数:

>>> ast.literal_eval(r'"hello",42')
('hello', 42)
>>> ast.literal_eval(r'"hello",42,helper="Larry, the \"wise\""')
Traceback (most recent call last):
  File "<unknown>", line 1
    "hello",42,helper="Larry, the \"wise\""
                     ^
SyntaxError: invalid syntax
Run Code Online (Sandbox Code Playgroud)

我想不出任何支持位置和关键字参数的 python 文字。


由于缺乏更好的想法,这里有一个使用的解决方案ast.parse

import ast

def parse_args(args):
    args = 'f({})'.format(args)
    tree = ast.parse(args)
    funccall = tree.body[0].value

    args = [ast.literal_eval(arg) for arg in funccall.args]
    kwargs = {arg.arg: ast.literal_eval(arg.value) for arg in funccall.keywords}
    return args, kwargs
Run Code Online (Sandbox Code Playgroud)

输出:

>>> parse_args(r'"hello",42,helper="Larry, the \"wise\""')
(['hello', 42], {'helper': 'Larry, the "wise"'})
Run Code Online (Sandbox Code Playgroud)