看面相对象语言的代码时候,一个痛点是要去捋清楚类的继承关系,靠肉眼去看用笔去记是一件很考验人耐性的事情,其实这可以通过写一个程序去完成,当然有的代码编辑器有这种功能,但不一定趁手好用。
前段时间,写了一个打印系统目录层次图的小程序,见打印目录树,那个实现不够通用,如果要打印类的继承关系图呢?或者要打印其他的树状对象呢?这里修改了一下,实现了一个基类,实现了一个子类用以打印类的继承关系图,供大家参考。
class attrs:
firstchld = False
lastchld = False
allchlddrawed = False
def __init__(self, name):
self.name = name
def setfirstchld(self):
self.firstchld = True
def setlastchld(self):
self.lastchld = True
def setallchlddrawed(self):
self.allchlddrawed = True
class treemap:
def __init__(self, root, reverse=True):
self.map = "";
self.chain = []
self.reverse = reverse
self.traverse(self.newattr(root))
print(self.map)
@classmethod
def newattr(cls, name):
pass
@staticmethod
def blanks(n):
return "".join([" " for i in range(n)])
def draw(self):
pass
def subattrs(self, attr):
pass
def traverse(self, attr):
self.chain.append(attr)
self.draw()
subattrs = self.subattrs(attr)
if not subattrs:
self.map += "\n"
self.chain.pop()
return
subattrs[0].setfirstchld()
subattrs[-1].setlastchld()
for d in subattrs:
self.traverse(d)
self.chain.pop()
基类treemap的traverse方法给出了绘制树的总体逻辑,就是以先根遍历访问树,每访问到一个新的结点,便进行树的局部绘制,获取树结点的子结点特征对象列表的方法subattrs和绘制树的方法draw均为抽象方法。
基类attrs用来记录树的一个结点的一些特征,它的name属性将被打印到树的图形中,它代表树的一个结点,它的firstchld属性表示自己是否是父结点的第一个孩子,它的lastchld属性表示自己是否是父结点的最后一个孩子,它的allchlddrawed属性表示自己的孩子结点是否已经全部绘制完毕。
要实现打印类的继承关系图的逻辑,需要实现基类treemap和attrs的子类,代码如下:
class typetreemap(treemap):
class typeattrs(attrs):
def __init__(self, typeobj):
self.type = typeobj
self.name = typeobj.__qualname__
@classmethod
def newattr(cls, typeobj):
return cls.typeattrs(typeobj)
def draw(self):
chain = self.chain
blanks = self.blanks
for i, d in enumerate(chain):
if d is chain[-1]:
self.map += d.name
if d.lastchld is True:
chain[i - 1].setallchlddrawed()
else:
if chain[-1].firstchld is not True:
if not d.allchlddrawed:
if chain[i + 1] is chain[-1]:
self.map += blanks(len(d.name)) + "|-"
else:
self.map += blanks(len(d.name)) + "| "
else:
self.map += blanks(len(d.name) + 2)
else:
if d is chain[-2]:
self.map += "--"
def subattrs(self, attr):
typeobj = attr.type
newattr = self.newattr
if self.reverse:
subattrs = [newattr(i) for i in typeobj.__bases__]
else:
if issubclass(typeobj, type):
subattrs = [newattr(i) for i in typeobj.__subclasses__(typeobj)]
else:
subattrs = [newattr(i) for i in typeobj.__subclasses__()]
#print(subattrs)
return subattrs
if __name__ == "__main__":
from collections.abc import Collection
typetreemap(Collection)
typetreemap(Collection, reverse=False)
调用的时候,reverse参数为True(默认值,可以不传)的时候,程序打印的是类及它的父类的关系图,如果想打印类与它的子类的关系图,reverse参数传False
要实现其他的树对象的打印,只需要同样实现基类treemap和attrs的子类,对上面的代码稍做修改就可以。