python - Python AST 中的 Expr 是什么?

标签 python abstract-syntax-tree

我正在研究用 Python 动态生成代码。

为此,我编写了一个辅助方法,它接收一串 Python 代码并转储出 AST。这是该方法:

# I want print treated as a function, not a statement.
import __future__
pfcf = __future__.print_function.compiler_flag

from ast import dump, PyCF_ONLY_AST

def d(s):
    print(dump(compile(s, '<String>', 'exec', pfcf|PyCF_ONLY_AST))

当我在一个简单的 Hello World 上运行这个函数时,它会输出以下内容(经过格式化以便于阅读):

d("print('Hello World!')")

Module(body=[Expr(value=Call(func=Name(id='print',
                                       ctx=Load()),
                             args=[Str(s='Hello World!')],
                             keywords=[],
                             starargs=None,
                             kwargs=None))])

我能够动态生成此代码并运行它 - 一切都很棒。

然后我尝试动态生成

print(len('Hello World!'))

应该很简单 - 只是另一个函数调用。这是我的代码动态生成的内容:

Module(body=[Expr(value=Call(func=Name(id='print',
                                       ctx=Load()),
                             args=[Expr(value=Call(func=Name(id='len',
                                                             ctx=Load()),
                                                   args=[Str(s='Hello World!')],
                                                   keywords=[],
                                                   starargs=None,
                                                   kwargs=None))],
                             keywords=[],
                             starargs=None,
                             kwargs=None))])

但是,运行它没有用。相反,我收到了这条消息:

TypeError: expected some sort of expr, but got <_ast.Expr object at 0x101812c10>

所以我运行了之前提到的辅助方法,看看它会输出什么:

d("print(len('Hello World!')")

Module(body=[Expr(value=Call(func=Name(id='print',
                                       ctx=Load()),
                             args=[Call(func=Name(id='len',
                                                  ctx=Load()),
                                        args=[Str(s='Hello World!')],
                                        keywords=[],
                                        starargs=None,
                                        kwargs=None)],
                             keywords=[],
                             starargs=None,
                             kwargs=None))])

我生成的(不起作用)和它生成的(起作用的)之间的区别在于,它们将 Call 直接传递给 args,而我将我的包裹在 Expr.

问题是,在第一行中,我需要将 Call 包装在 Expr 中。我很困惑 - 为什么有时需要将 Call 包装在 Expr 中,而其他时候则不需要? Expr 看起来应该只是 Call 继承自的抽象基类,但它在 Module 下的顶层是必需的。为什么?我缺少什么微妙的东西吗? Call 何时需要包裹在Expr 中以及何时可以直接使用有什么规则?

最佳答案

Expr 本身不是表达式的节点,而是表达式语句 --- 即仅由表达式组成的语句。这并不完全明显,因为抽象语法使用了三个不同的标识符 ExprExpressionexpr,它们的含义略有不同。

Statement 的语法允许一个 Expr 节点作为子节点,但是 Expr 节点的语法不允许另一个 Expr 节点作为子节点。换句话说,您所指的 args 值应该是表达式的列表,而不是 Expr 节点的列表。请参阅 the abstract grammar 的文档,其中包括:

stmt = FunctionDef(identifier name, arguments args, 
                            stmt* body, expr* decorator_list)
          | ClassDef(identifier name, expr* bases, stmt* body, expr* decorator_list)
          #...
          | Expr(expr value)

换句话说,一个可能的语句是Expr(blah),其中blah是匹配expr语法的东西。这是 Expr 在语法中的唯一用法,所以这就是 Expr 的全部; Expr 是一个可能的语句,仅此而已。语法的其他地方:

expr = BoolOp(boolop op, expr* values)
         | BinOp(expr left, operator op, expr right)
         # other stuff notably excluding Expr(...)
         | Call(expr func, expr* args, keyword* keywords,
             expr? starargs, expr? kwargs)

由于 Callargs 参数必须匹配 expr*,它必须是匹配 expr 的列表>。但是 Expr 节点不匹配 exprexpr 语法匹配表达式,而不是表达式语句。

请注意,如果您使用compile 的“eval”模式,它将编译一个表达式,而不是一个语句,因此Expr 节点将不存在,并且顶级 Module 节点将替换为 Expression:

>>> print(dump(compile('print("blah")', '<String>', 'eval', pfcf|PyCF_ONLY_AST)))
Expression(body=Call(func=Name(id='print', ctx=Load()), args=[Str(s=u'blah')], keywords=[], starargs=None, kwargs=None))

您可以看到 Expression 的主体是单个表达式(即 expr),因此 body 不是列表但直接设置为 Call 节点。但是,当您在“exec”模式下编译时,它必须为模块及其语句创建额外的节点,Expr 就是这样一个节点。

关于python - Python AST 中的 Expr 是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32429034/

相关文章:

c++ - clang : What does AST (abstract syntax tree) look like?

c++ - Roslyn 是否支持 C++ 生成语法树

python - 如何使用远程分支 pull 、推

python - 为什么我不能在 iPython 中使用 app.MainLoop()?

python - ffmpeg 将图像序列转换为视频

Python AST 模块无法检测到 "if"或 "for"

java - 模拟 java.lang.Thread 的最佳方法是什么?

python - 如何使用 python 连接多个 .wav 文件

python - 是否有标准方法可以在 python 应用程序之外存储数据库模式

parsing - 从源码到 AST 再到 CodeDom