【问题标题】:Python ast package: traversing object hierarchiesPython ast 包:遍历对象层次结构
【发布时间】:2012-02-24 04:29:22
【问题描述】:

以下是使用 astsymtable 的 Python 代码 sn-p 包。我正在尝试解析代码并检查类型。但是我 不明白如何遍历 objects 以获取实际变量 被引用。

下面的代码实现了一个NodeVisitor,一个函数被呈现给编译器,由编译器和ast walk进行解析。正在分析的函数 (eval_types) 传递了几个对象。

以下是构成示例的代码块。我为每个块添加了一些 cmets。要运行代码,需要重新组装“块”。

用于解析代码块的导入和取消缩进的函数。

import inspect
import ast
import symtable
from tokenize import generate_tokens, untokenize, INDENT
from cStringIO import StringIO

# _dedent borrowed from the myhdl package (www.myhdl.org)
def _dedent(s):
    """Dedent python code string."""

    result = [t[:2] for t in generate_tokens(StringIO(s).readline)]
    # set initial indent to 0 if any
    if result[0][0] == INDENT:
        result[0] = (INDENT, '')
    return untokenize(result)

以下是节点访问者,它具有通用的未处理和名称访问者重载。

class NodeVisitor(ast.NodeVisitor):
    def __init__(self, SymbolTable):
        self.symtable = SymbolTable
        for child in SymbolTable.get_children():
            self.symtable = child
            print(child.get_symbols())

    def _visit_children(self, node):
        """Determine if the node has children and visit"""
        for _, value in ast.iter_fields(node):
            if isinstance(value, list):
                for item in value:
                    if isinstance(item, ast.AST):
                        print('  visit item %s' % (type(item).__name__))
                        self.visit(item)

            elif isinstance(value, ast.AST):
                print('  visit value %s' % (type(value).__name__))
                self.visit(value)

    def generic_visit(self, node):
        print(type(node).__name__)
        self._visit_children(node)

    def visit_Name(self, node):
        print('  variable %s type %s' % (node.id,
                                         self.symtable.lookup(node.id)))
        print(dir(self.symtable.lookup(node.id)))

以下是一些简单的类,将在函数中使用,这些类将使用 AST 进行解析和分析。

class MyObj(object):
    def __init__(self):
        self.val = None

class MyObjFloat(object):
    def __init__(self):
        self.x = 1.

class MyObjInt(object):
    def __init__(self):
        self.x = 1

class MyObjObj(object):
    def __init__(self):
        self.xi = MyObjInt()
        self.xf = MyObjFloat()

下面是测试函数,eval_types函数就是要用AST分析的函数。

def testFunc(x,y,xo,z):

    def eval_types():
        z.val = x + y + xo.xi.x + xo.xf.x

    return eval_types

执行示例的代码,编译函数并分析。

if __name__ == '__main__':
    z = MyObj()
    print(z.val)
    f = testFunc(1, 2, MyObjObj(), z)
    f()
    print(z.val)
    s = inspect.getsource(f)
    s = _dedent(s)
    print(type(s))
    print(s)

    SymbolTable = symtable.symtable(s,'string','exec')
    tree = ast.parse(s)
    v = NodeVisitor(SymbolTable)
    v.visit(tree)

以下是直到第一次访问的示例输出。

Module
  visit item FunctionDef
FunctionDef
  visit value arguments
arguments
  visit item Assign
Assign
  visit item Attribute
Attribute
  visit value Name
  variable z type <symbol 'z'>
['_Symbol__flags', '_Symbol__name', '_Symbol__namespaces', 
 '_Symbol__scope', '__class__', '__delattr__', '__dict__', 
 '__doc__', '__format__', '__getattribute__', '__hash__', 
 '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', 
 '__repr__', '__setattr__', '__sizeof__', '__str__', 
 '__subclasshook__', '__weakref__', 'get_name', 'get_namespace', 
 'get_namespaces', 'is_assigned', 'is_declared_global', 
 'is_free', 'is_global', 'is_imported', 'is_local', 
 'is_namespace', 'is_parameter', 'is_referenced']

创建节点访问者似乎还不错,但我想不通 如何遍历对象层次结构。在一般情况下,变量 被访问的对象可能被深埋在一个对象中。如何获取从 ast 访问者访问的实际变量?我只看到一个 object 在节点上,但没有其他信息访问结果变量是什么。

【问题讨论】:

  • 不确定我是否遵循您的要求。是你找不到“.val”吗?您是否尝试过在该节点下递归?我已经很久没有使用它了,但我似乎记得在你的情况下,你需要从 visit_name 调用 visit_children。
  • @andrewcooke 感谢 cmets!是的,我想继续解析对象(z.val、xo.xi.x、xo.xf.x)并确定语句中使用的实际变量的更多信息。简单的情况是,z = x + y + xo_xi_x + xo_xf_x,其中变量没有嵌入(隐藏)在对象中,变量的属性/属性/类型可以在上面的代码 sn-p 中确定。我将尝试在这些类型的节点上显式调用 visit_children。再次感谢。
  • 在理解 AST 包方面取得小(缓慢)进展。对于此示例,对象“属性”将沿 visit_Attribute 向下移动。现在我只需要确定 visit_Name 何时有对象(有孩子?)然后在 visit_Attribute 中完成并向后工作(备份树)。
  • 祝你好运。文档很糟糕/根本不存在 - 你只需要阅读所有可以阅读的源代码......(但这是非常酷的代码)。

标签: python abstract-syntax-tree myhdl


【解决方案1】:

我不知道你是否还在寻找这个,但看起来你只需要添加一个visit_Attribute 并向后遍历。如果您将此添加到您的示例中:

def visit_Attribute(self, node):
    print('  attribute %s' % node.attr)
    self._visit_children(node)

那么xo.xf.x 的输出是:

Add
  visit value Attribute
  attribute x
  visit value Attribute
  attribute xf
  visit value Name
  variable xo type <symbol 'xo'>
  visit value Load

根据您要对此执行的操作,您只需将属性存储在列表中,直到遇到 Name,然后反转它们。

【讨论】:

    猜你喜欢
    • 2011-09-23
    • 2011-08-18
    • 1970-01-01
    • 1970-01-01
    • 2012-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多