利用 graphviz 可视化 Python MRO 层级关系

graphviz

graphviz 是一个很著名的绘图工具,graphviz 有一些 Python 的封装,通过 pip install graphviz 可以安装,文档参考 http://graphviz.readthedocs.org/en/latest/api.html

import graphviz
from graphviz import Digraph

dot = Digraph(comment='Flask')
dot.node('A', 'Python')
dot.node('B', 'Werkzeug')
dot.node('C', 'Flask')
dot.edges(['AB', 'AC'])
dot.edge('B', 'C', constraint='false')

display(dot)

svg

Python MRO

所谓 MRO 就是 Method Resolution Order,Python 的类通过 cls.__mro__ 方法可以查看 MRO

参考:https://www.python.org/download/releases/2.3/mro/

# Add test class, noqa please
class A(object): pass
class B(A): pass
class C(B): pass
class D(object): pass
class E(B, D): pass

print(C.__mro__)
print(E.__mro__)
  (<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
  (<class '__main__.E'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.D'>, <class 'object'>)

下面的代码简单的画出了 A,B,C,D,E 五个对象的层级关系,但是很不完善,没有递归查询

def render_mro(*classes):
    dot = Digraph()

    for cls in classes:
        for c in cls.__mro__[1:]:
            if c is object:
                continue
            dot.node(cls.__name__, cls.__name__)
            dot.node(c.__name__, c.__name__)
            dot.edge(c.__name__, cls.__name__)

    return dot

render_mro(A, B, C, D, E)

svg

现在封装成一个类,并加上递归查找

class MROGraph(object):

    def __init__(self, *classes, **kwargs):
        self.classes = classes
        self.dot = Digraph(**kwargs)
        self._classes = set()
        self._edges = set()
        self._nodes = set()

    def add_node(self, id_, label):
        if (id_, label) in self._nodes:
            return
        self.dot.node(id_, label)
        self._nodes.add((id_, label))

    def add_edge(self, left, right):
        if (left, right) in self._edges:
            return
        self.dot.edge(left, right)
        self._edges.add((left, right))

    def render_cls(self, cls):
        if cls.__name__ in self._classes:
            return
        self.add_node(cls.__name__, cls.__name__)
        for c in cls.__mro__[1:]:
            if c is object:
                continue
            self.add_node(c.__name__, c.__name__)
            self.add_edge(c.__name__, cls.__name__)
            self.render_cls(c)
        self._classes.add(cls.__name__)

    def render(self):
        for cls in self.classes:
            self.render_cls(cls)
        return self.dot


g = MROGraph(C, E)
g.render()

svg

可视化 Django 的 Class Based View

比如以 FormView 为例

from django.views.generic import View, TemplateView, DetailView, ListView, FormView
g = MROGraph(FormView, graph_attr={'size': '10,5', 'ratio': 'fill'})
g.render()

svg

g = MROGraph(DetailView, graph_attr={'size': '10,5', 'ratio': 'fill'})
g.render()

svg

from django.views.generic import View, TemplateView, DetailView, ListView, FormView
g = MROGraph(View, TemplateView, DetailView, ListView, FormView, graph_attr={'size': '10,5', 'ratio': 'fill'})
g.render()

svg

有待完善的问题

更多参考链接