什么是 Python AST 中的 Expr? [英] What is an Expr in Python AST?
问题描述
我正在研究在 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
不是一个表达式本身的节点,而是一个表达式语句——也就是说,一个语句由只是一种表达.这并不完全显而易见,因为抽象语法使用了三个不同的标识符 Expr
、Expression
和 expr
,它们的含义都略有不同.
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?夸格)
由于Call
的args
参数必须匹配expr*
,所以它必须是匹配expr
的事物列表>.但是 Expr
节点与 expr
不匹配;expr
语法匹配一个表达式,而不是一个表达式语句.
注意,如果你使用compile
的eval"模式,它会编译一个表达式,而不是一个语句,所以Expr
节点将不存在,而顶级Module
节点将被Expression
替换:
你可以看到 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屋!