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

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

问题描述

我正在研究在 Python 中动态生成代码.

为了帮助解决这个问题,我编写了一个辅助方法,它接收一串 Python 代码并转储出 AST.方法如下:

# 我希望将打印视为一个函数,而不是一个语句.进口__未来__pfcf = __future__.print_function.compiler_flag从 ast 导入转储,PyCF_ONLY_AST定义 d(s):打印(转储(编译(s,'','exec',pfcf|PyCF_ONLY_AST))

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

d("print('Hello World!')")模块(body=[Expr(value=Call(func=Name(id='print',ctx=负载()),args=[Str(s='Hello World!')],关键字=[],星参数=无,kwargs=无))])

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

然后我尝试动态生成

print(len('Hello World!'))

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

Module(body=[Expr(value=Call(func=Name(id='print',ctx=负载()),args=[Expr(value=Call(func=Name(id='len',ctx=负载()),args=[Str(s='Hello World!')],关键字=[],星参数=无,kwargs=无))],关键字=[],星参数=无,kwargs=无))])

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

TypeError: 期望某种类型的 expr,但在 0x101812c10 处得到了 <_ast.Expr 对象>

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

d("print(len('Hello World!')")模块(body=[Expr(value=Call(func=Name(id='print',ctx=负载()),args=[Call(func=Name(id='len',ctx=负载()),args=[Str(s='Hello World!')],关键字=[],星参数=无,kwargs=无)],关键字=[],星参数=无,kwargs=无))])

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

问题是,在第一行,我需要将 Call 包装在 Expr 中.我很困惑 - 为什么有时需要将 Call 包装在 Expr 中,但其他时候不需要?Expr 看起来应该只是一个Call 继承的抽象基类,但它在Module 正下方的顶层是必需的.为什么?我错过了一些微妙的东西吗?什么时候Call需要包裹在Expr中,什么时候可以直接使用?

解决方案

Expr 不是一个表达式本身的节点,而是一个表达式语句——也就是说,一个语句由只是一种表达.这并不完全显而易见,因为抽象语法使用了三个不同的标识符 ExprExpressionexpr,它们的含义都略有不同.

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

stmt = FunctionDef(标识符名称,参数 args,stmt* body, expr* decorator_list)|ClassDef(标识符名称,expr* bases,stmt* body,expr*decorator_list)#...|Expr(expr 值)

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

expr = BoolOp(boolop op, expr* values)|BinOp(expr left, operator op, expr right)# 其他特别不包括 Expr(...)|Call(expr func, expr* args, keyword* 关键字,表达式?星标,expr?夸格)

由于Callargs参数必须匹配expr*,所以它必须是匹配expr的事物列表>.但是 Expr 节点与 expr 不匹配;expr 语法匹配一个表达式,而不是一个表达式语句.

注意,如果你使用compile的eval"模式,它会编译一个表达式,而不是一个语句,所以Expr节点将不存在,而顶级Module节点将被Expression替换:

<预><代码>>>>打印(转储(编译('打印(废话")','<字符串>','eval',pfcf|PyCF_ONLY_AST)))表达式(body=Call(func=Name(id='print', ctx=Load()), args=[Str(s=u'blah')], 关键字=[], starargs=None, kwargs=None))

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

I'm working on dynamically generating code in Python.

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

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.

Then I tried to dynamically generate

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>

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

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.

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 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.

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)

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)

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.

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

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天全站免登陆