简体   繁体   English

Python对点图

[英]Python ast to dot graph

I'm analyzing the AST generated by python code for "fun and profit", and I would like to have something more graphical than "ast.dump" to actually see the AST generated. 我正在分析python代码生成的AST“有趣和利润”,我希望有一些比“ast.dump”更具图形性的东西来实际看到生成的AST。

In theory is already a tree, so it shouldn't be too hard to create a graph, but I don't understand how I could do it. 理论上已经是一棵树了,所以创建一个图表应该不会太难,但我不明白我是怎么做到的。

ast.walk seems to walk with a BFS strategy, and the visitX methods I can't really see the parent or I don't seem to find a way to create a graph... ast.walk似乎走了一个BFS策略,而visitX方法我真的看不到父或我似乎没有找到创建图的方法...

It seems like the only way is to write my own DFS walk function, is does it make sense? 似乎唯一的方法就是编写自己的DFS walk函数,它是否有意义?

If you look at ast.NodeVisitor, it's a fairly trivial class. 如果你看一下ast.NodeVisitor,它是一个相当简单的类。 You can either subclass it or just reimplement its walking strategy to whatever you need. 您既可以将其子类化,也可以将其步行策略重新实现为您需要的任何内容。 For instance, keeping references to the parent when nodes are visited is very simple to implement this way, just add a visit method that also accepts the parent as an argument, and pass that from your own generic_visit . 例如,在访问节点时保持对父节点的引用非常简单,只需添加一个也接受父节点作为参数的visit方法,并从你自己的generic_visit传递它。

PS By the way, it appears that NodeVisitor.generic_visit implements DFS, so all you have to do is add the parent node passing. PS顺便说一下,似乎NodeVisitor.generic_visit实现了DFS,所以你要做的就是添加父节点传递。

Fantastic, it works and it's really simple 太棒了,它很有效,而且非常简单

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)

So it's the same as a normal NodeVisitor, but I have a defaultdict where I add the type of the node for each son. 所以它与普通的NodeVisitor相同,但我有一个defaultdict,我为每个儿子添加节点的类型。 Then I pass this dictionary to pygraphviz.AGraph and I get my nice result. 然后我将这本字典传递给pygraphviz.AGraph,我得到了很好的结果。

The only problem is that the type doesn't say much, but on the other hand using ast.dump() is way too verbose. 唯一的问题是类型不多说,但另一方面使用ast.dump()太冗长了。

Best thing would be to get the actual source code for each node, is that possible? 最好的办法是获取每个节点的实际源代码,这可能吗?

EDIT: now it's much better, I pass in the constructor also the source code and I try to get the code line if possible, otherwise just print out the type. 编辑:现在它好多了,我传入构造函数也是源代码,如果可能我尝试获取代码行,否则只需打印出类型。

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM