【问题标题】:How can I obtain the full AST in Python?如何在 Python 中获得完整的 AST?
【发布时间】:2009-07-14 21:43:02
【问题描述】:

我喜欢 _ast 模块提供的选项,它真的很强大。有没有办法从中获取完整的 AST?

例如,如果我得到以下代码的 AST:

import os
os.listdir(".")

通过使用:

ast = compile(source_string,"<string>","exec",_ast.PyCF_ONLY_AST)

ast 对象的主体将有两个元素,一个 import 对象和一个 expr 对象。但是,我想更进一步,获取importlistdir的AST,也就是说,我想做_ast 下降到可能的最低水平。

我认为这种事情应该成为可能是合乎逻辑的。问题是如何

编辑:尽可能最低级别,我并不是说要访问“可见”的内容。我也想获得用于实现 listdir 的 AST:例如 stat 和其他可能为其执行的函数调用。

【问题讨论】:

  • 请记住,除非您的 Python 代码遵循几个约定,否则您必须实际执行代码才能找出在代码中的某个点使用了哪个模块/函数。跨度>
  • 那么我该怎么做呢?
  • 你不能,一般来说。你可以有类似的东西: import os;随机导入;如果 random.random() > .5: os.listdir = lambda *args: None; os.listdir(".");然后要弄清楚执行了哪些代码会有点棘手。但即使是方法调用也很棘手,因为您必须静态重建类层次结构和 mro。

标签: python abstract-syntax-tree


【解决方案1】:

您确实可以通过这种方式获得整棵树 - 一直到底部 - 但是,它被保存为一棵树,确切地说......所以在每个级别上,您必须明确访问所需的属性.例如(我将compile 结果命名为cf 而不是ast,因为这会隐藏标准库 ast 模块——我假设你只有 2.5 而不是 2.6,这就是为什么你使用较低的-level _ast 模块代替?)...:

>>> cf.body[0].names[0].name
'os'

这就是告诉您 import 语句正在导入名称 os 的原因(这只是因为 1 是 .body[0].names 字段的长度,即 import)。

在 Python 2.6 的模块 ast 中,您还可以获得帮助程序,让您更轻松地在树上导航(例如,通过 Visitor 设计模式)——但整个树都存在于 2.5 中(使用 @987654332 @) 或 2.5(使用 ast),并且在任何一种情况下都以完全相同的方式表示。

为了方便地访问树中的所有节点,在 2.6 中,使用模块 ast(没有前导下划线)和子类 ast.NodeVisitor(或等效地递归使用 ast.iter_child_nodesast.iter_fields)。当然,如果您因某种原因被困在 2.5 中,这些帮助程序可以在 _ast 之上的纯 Python 中实现。

【讨论】:

  • 快速提问:cf 代表什么?我在文档中的一些地方看到过这个缩写,你也使用它,但我不知道它代表什么。编译文件?代码格式化?代码文件?代码字段?编译字段?编译格式?
  • @ArtOfWarfare,我相信我想到了“编译形式”的一种止痛药缩写。
  • 谢谢,这是有道理的。我不喜欢在我的代码中使用不常见的缩写 - 我更喜欢简短且具有描述性的变量名称。所以我将其称为compiledForm 而不是cf
【解决方案2】:
py> ast._fields
('body',)
py> ast.body
[<_ast.Import object at 0xb7978e8c>, <_ast.Expr object at 0xb7978f0c>]
py> ast.body[1]
<_ast.Expr object at 0xb7978f0c>
py> ast.body[1]._fields
('value',)
py> ast.body[1].value
<_ast.Call object at 0xb7978f2c>
py> ast.body[1].value._fields
('func', 'args', 'keywords', 'starargs', 'kwargs')
py> ast.body[1].value.args
[<_ast.Str object at 0xb7978fac>]
py> ast.body[1].value.args[0]
<_ast.Str object at 0xb7978fac>
py> ast.body[1].value.args[0]._fields
('s',)
py> ast.body[1].value.args[0].s
'.'

HTH

【讨论】:

  • 我知道如何获得它。问题是,我如何获得 listdir 的 AST?不是函数参数,而是下面的实现。
  • listdir 没有 AST - 它是用 C 实现的。
  • 那么我将如何获得所有要获得的东西?如何让 _ast 递归到每个 python 实现的函数中?
  • @Geo,正如我在回答中提到的:升级到 2.6,使用不带前导下划线的模块 ast,并根据需要子类 ast.NodeVisitor(或等效地递归使用 ast.iter_child_nodes 并根据需要使用 ast.iter_fields )。
  • 您也不能“递归”到 Python 实现的函数中。首先,如果你有“foo.bar()”,你甚至不知道哪个 bar 被调用,因为后期绑定 - 你实际上必须运行该代码。即使对于模块级函数:仅编译对它们的调用,根本不考虑正在编译的实际函数。它可能不存在,即使它存在,你也可能只有函数的字节码。如果你想要函数的 AST,你需要找到模块源,然后自己编译。 “自动”从您的代码遍历到模块是不可能的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-19
  • 1970-01-01
  • 2017-09-09
相关资源
最近更新 更多