Python对点图

时间:2011-12-01 11:23:56

标签: python grammar abstract-syntax-tree

我正在分析由python代码生成的用于"有趣和利润的#34;并且我希望有更多的图形而不是" ast.dump"实际看到生成的AST。

理论上已经是一棵树了,所以创建图表并不难,但我不明白我是怎么做到的。

ast.walk似乎采用了BFS策略,而且我无法真正看到父母的访问方法,或者我似乎找不到创建图表的方法......

似乎唯一的方法就是编写自己的DFS walk函数,它是否有意义?

2 个答案:

答案 0 :(得分:6)

如果你看一下ast.NodeVisitor,它是一个相当简单的类。您既可以将其子类化,也可以将其步行策略重新实现为您需要的任何内容。例如,在访问节点时保持对父节点的引用非常简单,只需添加一个visit方法,该方法也接受父节点作为参数,并从您自己的generic_visit传递它。

P.S。顺便说一下,似乎NodeVisitor.generic_visit实现了DFS,所以你所要做的就是添加传递的父节点。

答案 1 :(得分:6)

太棒了,它很有效,而且非常简单

class AstGraphGenerator(object):

    def __init__(self):
        self.graph = defaultdict(lambda: [])

    def __str__(self):
        return str(self.graph)

    def visit(self, node):
        """Visit a node."""
        method = 'visit_' + node.__class__.__name__
        visitor = getattr(self, method, self.generic_visit)
        return visitor(node)

    def generic_visit(self, node):
        """Called if no explicit visitor function exists for a node."""
        for _, value in ast.iter_fields(node):
            if isinstance(value, list):
                for item in value:
                    if isinstance(item, ast.AST):
                        self.visit(item)

            elif isinstance(value, ast.AST):
                self.graph[type(node)].append(type(value))
                self.visit(value)

所以它与普通的NodeVisitor相同,但我有一个defaultdict,我为每个儿子添加节点的类型。 然后我将这本字典传递给pygraphviz.AGraph,我得到了很好的结果。

唯一的问题是该类型没有多说,但另一方面使用ast.dump()太冗长了。

最好的办法是获取每个节点的实际源代码,这可能吗?

编辑:现在它好多了,我传入构造函数也是源代码,如果可能的话我会尝试获取代码行,否则只需打印出类型。

class AstGraphGenerator(object):

    def __init__(self, source):
        self.graph = defaultdict(lambda: [])
        self.source = source  # lines of the source code

    def __str__(self):
        return str(self.graph)

    def _getid(self, node):
        try:
            lineno = node.lineno - 1
            return "%s: %s" % (type(node), self.source[lineno].strip())

        except AttributeError:
            return type(node)

    def visit(self, node):
        """Visit a node."""
        method = 'visit_' + node.__class__.__name__
        visitor = getattr(self, method, self.generic_visit)
        return visitor(node)

    def generic_visit(self, node):
        """Called if no explicit visitor function exists for a node."""
        for _, value in ast.iter_fields(node):
            if isinstance(value, list):
                for item in value:
                    if isinstance(item, ast.AST):
                        self.visit(item)

            elif isinstance(value, ast.AST):
                node_source = self._getid(node)
                value_source = self._getid(value)
                self.graph[node_source].append(value_source)
                # self.graph[type(node)].append(type(value))
                self.visit(value)