Python AST, ast.NodeTransformer,TypeError: stmt缺少必需字段'lineno

9 投票
2 回答
4553 浏览
提问于 2025-04-18 07:17

我非常希望能学到一些有用的东西,因为到目前为止,我一直在盲目摸索。

问题出在Python的 ast.NodeTransformer 上。我在想,是否可以通过这种方式给现有的类添加一个函数,而不会感到困惑。

这是我到目前为止的做法。

import ast, inspect, cla # cla is a name of class to which we want to add a new function

klass = inspect.getsource(cla)
tree = ast.parse(klass)
st = '''def function(): return 1'''
Foo = ast.parse(st)

class AddFunc(ast.NodeTransformer):
      def visit_ClassDef(self, node):
          return node, node.body + Foo.body
          self.generic_visit(node)

inst = AddFunc()
stuff = i.visit(tree)

# now the trouble begins, a compiling..
co = compile(stuff, filename='<ast>', mode='exec')

# i get TypeError: required "lineno" missing from stmt

我尝试过(你可能猜到的,没成功)使用 ast 库里的辅助函数 ast.fix_missing_locations()ast.copy_location() 来处理这个问题,但大多数情况下我要么是在猜,要么是遇到了 AttributeError,这个错误出现在 AddFunc 类里面的一个元组上。有没有人知道该怎么解决这个问题呢?

2 个回答

7

我在推特上找到了答案:https://twitter.com/mitsuhiko/status/91169383254200320。mitsuhiko说:“TypeError: required field 'lineno' missing from stmt”——其实你想说的是“元组不是一个语句”。#helpfulpython

你在这里返回的是元组,但你应该返回的是抽象语法树(ast)。

class AddFunc(ast.NodeTransformer):
  def visit_ClassDef(self, node):
      node.body += Foo.body
      return node
10

kolko的回答是对的,在这种情况下,你需要返回一个AST节点。这里的Foo是一个模块,它的主体应该是构建函数的语句,这些语句可以简单地插入到类定义中。

理解ast.NodeTransformer的工作方式很重要:

  • 它会根据具体的类名调用visit方法,而不考虑继承关系
  • 它会检查返回值是否是isinstance(AST)isinstance(list),其他的返回值会被忽略!

即使你使用ast.fix_missing_locations来添加位置信息,你也可能会遇到这个错误,或者类似的“expr缺少行号”的错误。

如果出现这种情况,说明你的AST中有一个元素在那个位置是不允许的。你可以使用astunparse.dump来仔细检查结构。在模块主体内,只能有语句。在函数定义内,也只能有语句。还有一些特定的位置可以放表达式。

要解决这个问题,写出你期望生成的Python代码,解析它,转储它,并与正在生成的内容进行对比。

撰写回答