利用 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)
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)
现在封装成一个类,并加上递归查找
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()
可视化 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()
g = MROGraph(DetailView, graph_attr={'size': '10,5', 'ratio': 'fill'})
g.render()
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()
有待完善的问题
- 显示 Class 的类签名,比如标签显示为 E(B, D)
- meta class 的支持
- 上述代码通过
cls.__name__
确定类的唯一性,这样合理吗? - old style class 的支持