python树形数据结构工具类

树形工具类用到的辅助方法

异常、判断空的方法、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()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值