如何使用ast.NodeTransformer将列表推导式转换为for循环?

3

我正在尝试使用 ast.NodeTransformer 将列表推导式转换为 for 循环。之后,我将对 Python 进行类型分析。

我知道当遇到 ast 节点 ast.ListComp 时,它必须被转换为两个 ast 节点:第一个节点是 ast.Assign,第二个节点是 ast.For。但是根据文档 ast.NodeTransformerast.ListComp 只能转换为 一个 ast 节点,而不能将 ast.Assignast.For 插入 AST 中。

目前,我可以创建一个等效的 for 循环。但是我还需要一个赋值语句来将列表初始化为 []。我该如何绕过这个问题?

以下是一个示例:

a=[x for x in [1,2,3]] # One node in AST

to

a=[] # One node in AST
for x in [1,2,3]:  # Another one in AST
    a.append(x)

在Python中,生成器是一种一等对象(具有类型);通常情况下,它不能被消除。您可以创建一个并将其传递给函数:foo(x+2 for x in arr)。实际上,对于[expr for variable in generator]的等效操作是使用内置的列表构造函数:list(expr for variable in generator)。 (如果expr只是变量x for x in...,则可以将其丢弃:list(generator)。不过我不知道这是否真正对您有所帮助 :-( - rici
1个回答

1

您可以递归遍历 AST,当在赋值语句中遇到列表推导式 (ast.ListComp) 时,您可以删除父级赋值对象并将从推导式派生的标准 for 循环插入到主体作用域中:

import ast
def comp_to_expl(tree):
   if hasattr(tree, 'body'):
      i = 0
      while i < len(tree.body):
         if isinstance(a:=tree.body[i], ast.Assign) and isinstance(a.value, ast.ListComp):
            tree.body = tree.body[:i] + \
                [ast.Assign(
                   targets=[ast.Name(id = a.targets[0].id)], value = ast.List(elts = []),
                   lineno = a.lineno
                )] + \
                [ast.For(
                   target = a.value.generators[0].target,
                   iter = a.value.generators[0].iter,
                   body = [ast.Expr(
                             value = ast.Call(
                                       func = ast.Attribute(value = ast.Name(id = a.targets[0].id), attr = 'append', ctx = ast.Load()),
                                       args = [a.value.elt],
                                       keywords = []
                       ))],
                   lineno = a.lineno+1,
                   orelse = [],
                  )] + \
                tree.body[i+1:]   
            i += 1   
         i += 1
   for i in getattr(tree, '_fields', []):
      if isinstance(v:=getattr(tree, i, None), list):
         for i in v: 
            comp_to_expl(i)
      elif isinstance(v, ast.AST):
         comp_to_expl(v)
         
synt = ast.parse('a=[x for x in [1,2,3]]')
comp_to_expl(synt)
print(ast.unparse(synt))

输出:

a = []
for x in [1, 2, 3]:
    a.append(x)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接