什么是Python AST中的Expr? [英] What is an Expr in Python AST?

查看:99
本文介绍了什么是Python AST中的Expr?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm working on dynamically generating code in Python.

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

To aid with this, I wrote a helper method which takes in a string of Python code and dumps out the AST. Here's that method:

# 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上运行此功能时,它会吐出以下内容(格式化以便于阅读):

When I run this function on a simple Hello World, it spits out the following (formatted for easier reading):

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))])

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

I was able to dynamically generate this code and run it - everything was great.

然后我尝试动态生成

print(len('Hello World!'))

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

Should be pretty easy - just another function call. Here's what my code dynamically generated:

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))])

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

Running it didn't work, though. Instead, I got this message:

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

所以我运行了前面提到的helper方法以查看其输出内容:

So I ran my helper method previously mentioned to see what it would output:

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 中。

The difference between what I'm generating (which doesn't work) and what it generates (which works), is that they passed Call directly to args, whereas I wrapped mine in Expr.

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

The problem is, in the very first line, I needed to wrap Call in an Expr. I'm confused - why is it sometimes necessary to wrap a Call in an Expr but not other times? Expr seems like it should be just an abstract base class which Call inherits from, but it's required at the top level right under the Module. Why? Is there something subtle I'm missing? What are the rules for when Call needs to be wrapped in an Expr and when it can be used directly?

推荐答案

Expr 本身不是表达式的节点,而是表达式-statement ---即仅包含表达式的语句。这不是很明显,因为抽象语法使用三个不同的标识符 Expr Expression expr ,所有含义都稍有不同。

Expr is not the node for an expression per se but rather an expression-statement --- that is, a statement consisting of only an expression. This is not totally obvious because the abstract grammar uses three different identifiers Expr, Expression, and expr, all meaning slightly different things.

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

The grammar of Statement allows an Expr node as a child, but the grammar of an Expr node doesn't allow another Expr node as a child. In other words, the args value you are referring to is supposed to be a list of things-that-are-expressions, not a list of Expr nodes. See the documentation of the abstract grammar, which includes:

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 expr 的语法相匹配。这是语法中 Expr 的唯一用法,因此,所有 Expr 都可以使用; Expr 是可能的语句,仅此而已。语法中的其他地方:

In other words, a possible statement is Expr(blah), where blah is something matching the grammar of expr. This is the only use of Expr in the grammar, so this is all an Expr can be; an Expr is a possible statement and nothing else. Elsewhere in the grammar:

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)

由于 Call args 自变量必须与 expr * ,它必须是与 expr 匹配的事物的列表。但是 Expr 节点与 expr 不匹配; expr 语法匹配表达式,而不是表达式语句。

Since the args argument of Call must match expr*, it must be a list of things matching expr. But an Expr node doesn't match expr; the expr grammar matches an expression, not an expression-statement.

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

Note that if you use the "eval" mode of compile, it will compile an expression, not a statement, so the Expr node will be absent, and the top-level Module node will be replaced by 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))

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

You can see that the body of an Expression is a single expression (i.e., an expr), so body is not a list but is set directly to the Call node. When you compile in "exec" mode, though, it has to create extra nodes for the module and its statements, and Expr is such a node.

这篇关于什么是Python AST中的Expr?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆