树形工具类用到的辅助方法
异常、判断空的方法、copy包
import copy
'''
系统异常类
'''
class EamsException(Exception):
code = 0
msg = None
data = None
def __init__(self, msg=None, data=None, code=0):
self.msg = msg
self.data = data
self.code = code
def isEmpty(obj):
if obj is None:
return True
if type(obj) is int:
return False
if type(obj) is float:
return False
if type(obj) is complex:
return False
if type(obj) is bool:
return False
return len(obj) == 0
def isNotEmpty(obj):
return not isEmpty(obj)
树形工具类
'''
树工具类,通过用层级关系的数据列表构建数据列表数据构成树,并提供对树的一系列操作方法;
注意:
1:构建树时,数据列表会被深度复制到工具类对象的nodeList属性中;
2:所有获取方法(公有方法)返回的是工具类对象属性的深度复制
总结:构建树时,数据列表的数据结构不会发生改变;操作从获取方法得到的节点不会改变工具类对象中对应的节点
'''
class TreeUtil:
def __init__(self, nodeList=None, idField="id", pidField="pid", levelField="level", rootIdField="rootId",
childListField="childList", rootParentValue=None, scrutiny=True, additionalFields={}):
self.idField = idField # 节点id属性名称
self.pidField = pidField # 节点父级id属性名称
self.levelField = levelField # 节点层级属性名称
self.rootIdField = rootIdField # 节点根节点属性名称
self.childListField = childListField # 节点孩子节点属性名称
self.rootParentValue = rootParentValue # 根节点的父级id标识,用这个属性值确定根节点
self.scrutiny = scrutiny # 严格检查标识:用于决定发现游离节点时是否抛异常;游离节点:父级id指向的节点不存在
self.additionalFields = additionalFields # 额外新增的属性及其默认值
self.max_level = 0 # 所有树的最大层级
self.nodeList = [] # 所有节点列表
self.nodeMap = {} # 所有节点map,key为节点id,value为节点本身;方便查询节点
self.treeNumber = 0 # 树的数量
self.rootIdList = [] # 根节点id列表
self.__setNodeList(nodeList)
self.__setNodeMap()
self.__buildTrees()
self.__checkFreeNode()
def __setNodeList(self, nodeList): # 设置nodeMap,key为节点id,value为节点本身
if isNotEmpty(nodeList):
for node in nodeList:
temp = copy.deepcopy(node)
if isEmpty(temp.get(self.pidField)):
temp[self.pidField] = None
self.nodeList.append(temp)
def __setNodeMap(self): # 设置nodeMap,key为节点id,value为节点本身
for node in self.nodeList:
self.nodeMap[node.get(self.idField)] = node
def __buildTrees(self): # 构建树结构
rootNodeList = self.__findRootNode()
for root in rootNodeList:
root[self.rootIdField] = root.get(self.idField)
self.__buildTree(root, 1)
self.treeNumber += 1
self.rootIdList.append(self.idField)
def __findRootNode(self): # 查询根节点
rootNodeList = []
for node in self.nodeList:
if node.get(self.pidField) == self.rootParentValue:
rootNodeList.append(node)
return rootNodeList
def __buildTree(self, rootNode, level): # 从根节点开始构建此根节点对应的树
rootId = rootNode.get(self.idField)
childList = []
for node in self.nodeList:
if node.get(self.pidField) == rootId:
childList.append(node)
rootNode[self.childListField] = childList
rootNode[self.levelField] = level
keys = self.additionalFields.keys()
if isNotEmpty(keys):
for k in keys:
rootNode[k] = self.additionalFields.get(k)
if level > self.max_level:
self.max_level = level
if isNotEmpty(childList):
for node in childList:
node[self.rootIdField] = rootNode.get(self.rootIdField)
self.__buildTree(node, level + 1)
def __checkFreeNode(self): # 检查游离的节点:有些节点有父级id,但是所有节点中却没发现他们的父节点
freeNodeList = []
for node in self.nodeList:
pid = node.get(self.pidField)
if pid != self.rootParentValue and self.nodeMap.get(pid) is None:
freeNodeList.append(node)
if isNotEmpty(freeNodeList):
if self.scrutiny:
raise EamsException(f"没有发现以下节点父级id指向的节点{freeNodeList}")
else:
print(f"没有发现以下节点父级id指向的节点{freeNodeList}")
def getTrees(self): # 获取所有树
return copy.deepcopy(self.__findRootNode())
def getTreeNodeList(self, rootId): # 获取指定节点及其子节点
treeNodeList = []
root = self.nodeMap.get(rootId)
if isEmpty(root):
return treeNodeList
treeNodeList.append(root)
childList = root.get(self.childListField)
if isNotEmpty(childList):
for child in childList:
treeNodeList.extend(self.getTreeNodeList(child.get(self.idField)))
return copy.deepcopy(treeNodeList)
def getNode(self, nodeId): # 获取指定节点
return copy.deepcopy(self.nodeMap.get(nodeId))
def getChildList(self, nodeId): # 获取孩子节点
node = self.nodeMap.get(nodeId)
if isEmpty(node):
return None
return copy.deepcopy(node.get(self.childListField))
def getOffspringList(self, nodeId): # 获取后代节点
root = self.nodeMap.get(nodeId)
treeNodeList = self.getTreeNodeList(nodeId)
treeNodeList.remove(root)
return copy.deepcopy(treeNodeList)
def getParent(self, nodeId): # 获取直接父节点
node = self.nodeMap.get(nodeId)
return copy.deepcopy(self.nodeMap.get(node.get(self.pidField)))
def getParentList(self, nodeId): # 获取所有父节点
node = self.nodeMap.get(nodeId)
pid = node.get(self.pidField)
parentList = []
while pid != self.rootParentValue:
parent = self.nodeMap.get(pid)
parentList.append(parent)
pid = parent.get(self.pidField)
return copy.deepcopy(parentList)
def getBrotherList(self, nodeId): # 获取兄弟节点
node = self.nodeMap.get(nodeId)
parent = self.nodeMap.get(node.get(self.pidField))
brotherList = copy.deepcopy(parent.get(self.childListField))
brotherList.remove(node)
return brotherList
def getLeafList(self): # 获取叶子节点
leafNodeList = []
for node in self.nodeList:
if isEmpty(node.get(self.childListField)):
leafNodeList.append(node)
return copy.deepcopy(leafNodeList)
def getMaxLevel(self): # 获取最大层级
return copy.deepcopy(self.max_level)
def getLevelList(self, level): # 获取某层级节点
if level > self.max_level:
return None
levelList = []
for node in self.nodeList:
if node.get(self.levelField) == level:
levelList.append(node)
return copy.deepcopy(levelList)
def containsNode(self, nodeId): # 判断是否包含某节点
return nodeId in self.nodeMap
def getTreeNumber(self): # 获取树的数量
return copy.deepcopy(self.treeNumber)
def getAllNode(self): # 获取所有节点
return copy.deepcopy(self.nodeList)
def isRootNode(self, rootId): # 判断是否是根节点
return rootId in self.rootIdList
测试例子
def test0():
# dataList = [{"id": "01", "pid": None, "name": "模块1"},{"id": "02", "pid": None, "name": "模块2"},
# {"id": "011", "pid": "01", "name": "模块1-权限1"},{"id": "012", "pid": "01", "name": "模块1-权限2"},
# {"id": "021", "pid": "02", "name": "模块2-权限1"},{"id": "022", "pid": "02", "name": "模块2-权限2"}]
dataList = [{"qid": "01", "fid": None, "name": "模块1"}, {"qid": "02", "pid": None, "name": "模块2"},
{"qid": "011", "fid": "01", "name": "模块1-权限1"}, {"qid": "012", "fid": "01", "name": "模块1-权限2"},
{"qid": "021", "fid": "02", "name": "模块2-权限1"}, {"qid": "022", "fid": "02", "name": "模块2-权限2"},
{"qid": "0111", "fid": "011", "name": "模块1-权限1-权限1"}, {"qid": "0112", "fid": "011", "name": "模块1-权限1-权限2"},
{"qid": "0211", "fid": "021", "name": "模块2-权限1-权限1"}, {"qid": "0212", "fid": "021", "name": "模块2-权限2-权限2"},]
treeUtil = TreeUtil(nodeList=dataList, idField="qid", pidField="fid")
trees = treeUtil.getTrees()
import json
for tree in trees:
print(json.dumps(tree, ensure_ascii=False))
# output: 'hi yasoob'
nodeId = "011"
maxLevel = treeUtil.getMaxLevel()
print(f"最大层级:{maxLevel}")
node = treeUtil.getNode(nodeId)
print(f"id为{nodeId}的节点信息:{json.dumps(node, ensure_ascii=False)}")
TreeNodeList = treeUtil.getTreeNodeList(nodeId)
print(f"以id等于{nodeId}的节点为根的树的所有节点:{json.dumps(TreeNodeList, ensure_ascii=False)}")
ChildList = treeUtil.getChildList(nodeId)
print(f"孩子节点{json.dumps(ChildList, ensure_ascii=False)}")
OffspringList = treeUtil.getOffspringList(nodeId)
print(f"后代节点:{json.dumps(OffspringList, ensure_ascii=False)}")
Parent = treeUtil.getParent(nodeId)
print(f"直接父节点:{json.dumps(Parent, ensure_ascii=False)}")
ParentList = treeUtil.getParentList(nodeId)
print(f"所有父节点:{json.dumps(ParentList, ensure_ascii=False)}")
BrotherList = treeUtil.getBrotherList(nodeId)
print(f"兄弟节点:{json.dumps(BrotherList, ensure_ascii=False)}")
LeafList = treeUtil.getLeafList()
print(f"叶子节点:{json.dumps(LeafList, ensure_ascii=False)}")
LevelList = treeUtil.getLevelList(2)
print(f"层级等于2的节点:{json.dumps(LevelList, ensure_ascii=False)}")
for data in dataList:
print(json.dumps(data, ensure_ascii=False))
if __name__ == '__main__':
test0()