什么是Python AST中的Expr?


问题内容

我正在使用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

问题是,在第一行,我需要包裹CallExpr。我很困惑-为什么有时需要Call在一个Expr而不是其他时间包装a
Expr似乎它应该只是一个Call继承自其的抽象基类,但是它在之下的顶层是必需的Module。为什么?我想念些什么吗?什么时候Call需要包装在Expr何时可以直接使用的规则是什么?


问题答案:

Expr不是表达式本身的节点,而是表达式语句—
即仅包含表达式的语句。因为抽象语法使用三种不同的标识符,这是不完全明显ExprExpressionexpr,所有的意义略有不同的东西。

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

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)

由于args参数Call必须匹配expr*,它必须是匹配的事物的清单expr。但是一个Expr节点不匹配expr;
expr语法相匹配的表现,而不是一个表达式语句。

请注意,如果使用的“
eval”模式compile,它将编译一个表达式,而不是一条语句,因此该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))

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