什么是Python AST中的Expr?

时间:2015-09-06 22:49:49

标签: 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以及什么时候可以直接使用?

2 个答案:

答案 0 :(得分:8)

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),其中blahexpr的语法相匹配。这是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的{​​{1}}参数必须与Call匹配,因此它必须是与expr*匹配的列表。但expr节点与Expr不匹配; expr语法匹配表达式,而不是表达式语句。

请注意,如果您使用&#34; eval&#34;模式expr,它将编译表达式而不是语句,因此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)) 的正文是单个表达式(即Expression),因此expr不是列表,而是直接设置为{{1}节点。当您编译&#34; exec&#34;但是,它必须为模块及其语句创建额外的节点,而body就是这样一个节点。

答案 1 :(得分:2)

同意@BreBarn的说法:

“当一个表达式(如函数调用)本身作为语句(表达式语句)出现时,其返回值未被使用或存储,它将被包装在此容器中。”

由于您将len函数的结果用于print,因此从技术上讲,它在AST意义上不是Expression

有关详细信息,请参阅此处:https://greentreesnakes.readthedocs.org/en/latest/nodes.html#expressions