在使用ast时,有没有一种简单的方法可以返回多个节点来替换单个节点。节点变压器?例如,假设我想重写表单的所有表达式
f(g())
到_x1 = g(); g(_x1)
如果visit_Expr
可以返回多个节点而不是单个节点,则很容易做到这一点。不过,我似乎无法让它工作,所以我认为这不是这样做的方法。任何建议将不胜感激。
[Update]
作为更新,我有一个工作版本,它将新旧节点累积在一个列表中,并将它们分配给封闭范围节点的主体(例如For
、While
、Module
节点等(。这绝对是一种笨拙的方法,并且怀疑有更好的方法。我会保留这个,以防有人知道。
[final update]
查看文档以了解NodeTransformer
,如果节点是语句集合的一部分,则实际上完全可以返回节点列表。
对于语句节点,您可以返回新节点的列表。这允许您将单个语句替换为多个语句。引用文档:
对于属于语句集合(适用于所有语句节点(的节点,访问者还可以返回节点列表,而不仅仅是单个节点。
对于表达式,有一个顶级Expr()
表达式语句节点:
>>> ast.dump(ast.parse('f(g())'))
"Module(body=[Expr(value=Call(func=Name(id='f', ctx=Load()), args=[Call(func=Name(id='g', ctx=Load()), args=[], keywords=[])], keywords=[]))])"
>>> import ast
>>> ast.dump(ast.parse('f(g())'))
"Module(body=[Expr(value=Call(func=Name(id='f', ctx=Load()), args=[Call(func=Name(id='g', ctx=Load()), args=[], keywords=[])], keywords=[]))])"
因此,您所要做的就是提供一个返回 2 个语句节点列表的visit_Expr
处理程序;第一个是调用g()
的赋值(Assign()
语句节点(,第二个是传入新变量名的调用f
(另一个Expr()
语句节点(。
我会将状态保留在转换器子类中,该子类在您输入和离开表达式语句上下文时设置标志,并跟踪该上下文下的调用堆栈。然后返回到visit_Expr
时,返回新设置:
self._expr_statement = False
def visit_Expr(node):
self._expr_statement = True
self.generic_visit(node)
self._expr_statement = False
if <specific state on self matches expectations>:
tempvar = <generated_new_name>
return [
Assign([Name(tempvar, Store())], <inner_call>),
Expr(Call(
<outer_function_name_expr>,
args=[Name(tempvar, Load())],
keyword=[]))
]
else:
# no replacement takes place
return node
NodeTransformer then uses that list of elements to replace the previous
Expr((' 节点。
请注意,您必须调用self.generic_visit(node)
仍然以确保仍处理嵌套节点;然后将为这些嵌套节点调用进一步visit_*
方法。这样,您就可以在visit_Call
方法中检查self._expr_statement
标志,然后测试是否存在嵌套调用,然后在self
上存储足够的上下文以供返回visit_Expr()
方法。