Python AST 中的 Expr 是什么?
What is an Expr in Python AST?
我正在 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
继承的抽象基础 class,但它在 Module
正下方的顶层是必需的。为什么?我缺少什么微妙的东西吗? Call
什么时候需要包裹在一个Expr
中,什么时候可以直接使用,有什么规则?
Expr
本身不是表达式的节点,而是表达式语句 --- 即仅由表达式组成的语句。这并不完全明显,因为抽象语法使用了三个不同的标识符 Expr
、Expression
和 expr
,它们的含义略有不同。
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)
,其中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)
由于 Call
的 args
参数必须匹配 expr*
,因此它必须是匹配 expr
的事物列表。但是 Expr
节点不匹配 expr
; expr
语法匹配表达式,而不是表达式语句。
注意,如果使用compile
的"eval"模式,编译的是表达式,而不是语句,所以Expr
节点将不存在,top-级别 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
),因此 body
不是列表,而是直接设置为 Call
节点。但是,当您在 "exec" 模式下编译时,它必须为模块及其语句创建额外的节点,而 Expr
就是这样一个节点。
同意@BreBarn 所说的:
"When an expression, such as a function call, appears as a statement by itself (an expression statement), with its return value not used or stored, it is wrapped in this container."
由于您将 len
函数的结果用于 print
,因此在 AST 意义上它在技术上不是 Expression
。
有关详细信息,请参阅此内容:https://greentreesnakes.readthedocs.org/en/latest/nodes.html#expressions
我正在 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
继承的抽象基础 class,但它在 Module
正下方的顶层是必需的。为什么?我缺少什么微妙的东西吗? Call
什么时候需要包裹在一个Expr
中,什么时候可以直接使用,有什么规则?
Expr
本身不是表达式的节点,而是表达式语句 --- 即仅由表达式组成的语句。这并不完全明显,因为抽象语法使用了三个不同的标识符 Expr
、Expression
和 expr
,它们的含义略有不同。
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)
,其中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)
由于 Call
的 args
参数必须匹配 expr*
,因此它必须是匹配 expr
的事物列表。但是 Expr
节点不匹配 expr
; expr
语法匹配表达式,而不是表达式语句。
注意,如果使用compile
的"eval"模式,编译的是表达式,而不是语句,所以Expr
节点将不存在,top-级别 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
),因此 body
不是列表,而是直接设置为 Call
节点。但是,当您在 "exec" 模式下编译时,它必须为模块及其语句创建额外的节点,而 Expr
就是这样一个节点。
同意@BreBarn 所说的:
"When an expression, such as a function call, appears as a statement by itself (an expression statement), with its return value not used or stored, it is wrapped in this container."
由于您将 len
函数的结果用于 print
,因此在 AST 意义上它在技术上不是 Expression
。
有关详细信息,请参阅此内容:https://greentreesnakes.readthedocs.org/en/latest/nodes.html#expressions