美文网首页
使用python3处理树形结构数据,tree data 树形数据

使用python3处理树形结构数据,tree data 树形数据

作者: SystemLight | 来源:发表于2020-07-24 23:32 被阅读0次
    from tornado.template import Template, Loader
    
    import os
    import re
    from typing import List, Optional, Callable, TypeVar
    
    T = TypeVar("T")
    ParseCallable = Callable[["TreeOperate", List[T], int], T]
    
    
    def require(path):
        """
    
        有时你可能只是需要从文件中读取到json数据,这是require函数将根据
        获取到的path,返回dict对象,相当方便,该函数同样类似于json.load
    
        :param path:
        :return: dict
    
        """
        import json
        fp = open(path, "r")
        data = fp.read()
        fp.close()
        try:
            return json.loads(data)
        except json.decoder.JSONDecodeError:
            return {}
    
    
    class GenerateCodeEngine:
        """
    
        生成代码引擎类
    
        使用方法::
    
            gec = GenerateCodeEngine()
            gec.catch_write("index.html", "template.html", {
                "anthor": "systemlight"
            })
    
        """
    
        def __init__(self, template_root_path=""):
            """
    
            构造函数
    
            :param template_root_path: 模板根目录
    
            """
            self.glob_content = {}
            self.template_root_path = template_root_path
    
            self.__start_match_tag = r"//start_user_code"
            self.__end_match_tag = r"//end_user_code"
            self.catch_match = r"{}(.*?){}".format(self.__start_match_tag, self.__end_match_tag)
    
        @property
        def start_match_tag(self):
            return self.__start_match_tag
    
        @start_match_tag.setter
        def start_match_tag(self, value):
            self.__start_match_tag = value
            self.catch_match = r"{}(.*?){}".format(self.__start_match_tag, self.__end_match_tag)
    
        @property
        def end_match_tag(self):
            return self.__end_match_tag
    
        @end_match_tag.setter
        def end_match_tag(self, value):
            self.__end_match_tag = value
            self.catch_match = r"{}(.*?){}".format(self.__start_match_tag, self.__end_match_tag)
    
        def register_glob_content(self, name, value):
            """
    
            注册全局方法或者变量,每个模板渲染时都将附带该内容
    
            :param name: 名称
            :param value: 内容
            :return: None
    
            """
            self.glob_content[name] = value
    
        def render(self, template_path, kwargs=None):
            """
    
            根据模板渲染并生成字符串返回
    
            :param template_path: 模板文件路径
            :param kwargs: 包含写入模板中的变量数据和函数等
            :return: 渲染后的内容
    
            """
            template_path = os.path.join(self.template_root_path, template_path)
            if kwargs is None:
                kwargs = {}
            with open(template_path, "r", encoding="utf-8") as fp:
                temp = Template(fp.read(), autoescape=None, loader=Loader(self.template_root_path))
            glob_content = {**self.glob_content, **kwargs}
            return temp.generate(**glob_content)
    
        def write(self, path, template_path, kwargs=None):
            """
    
            将渲染内容希尔到文件当中
    
            :param path: 目标文件路径
            :param template_path: 模板文件路径
            :param kwargs: 包含写入模板中的变量数据和函数等
            :return: None
    
            """
            with open(path, "w", encoding="utf-8") as fp:
                fp.write(self.render(template_path, kwargs).decode())
    
        def catch_write(self, path, template_path, kwargs=None):
            """
    
            捕获用户代码写入方法,执行写入之前会先匹配用户代码
    
            :param path: 目标文件路径
            :param template_path: 模板文件路径
            :param kwargs: 其它额外参数,参考catch_user_code方法,包含写入模板中的变量数据和函数等
            :return: None
    
            """
            if kwargs is None:
                kwargs = {}
            user_code = self.catch_user_code(
                path=path,
                match=kwargs.get("match", None),
                code_count=kwargs.get("code_count", 1),
            )
            kwargs["user_code"] = user_code
            self.write(path, template_path, kwargs)
    
        def catch_user_code(self, path, match=None, code_count=1):
            """
    
            捕获目标路径文件中的用户代码
    
            :param path: 目标文件路径
            :param match: 匹配用户代码规则
            :param code_count: 用户代码数量
            :return: 匹配结果列表
    
            """
            if match is None:
                match = self.catch_match
            if not os.path.exists(path):
                return [""] * code_count
            with open(path, "r", encoding="utf-8") as fp:
                content = fp.read()
            result = re.findall(match, content, re.S)
            result = list(map(lambda v: v.strip("\n "), result))
            size = len(result)
            if size < code_count:
                return result + [""] * (code_count - size)
            return result
    
    
    class TreeOperate:
        """
        
        TreeOperate允许你操作一颗树型结构数据,支持数据导入和导出,数据必须含有key唯一标识,
        子元素必须存储在children键值下
        示例内容::
            _data = {
                "key": "1",
                "title": "root",
                "children": [
                    {"key": "2", "title": "2", "children": [
                        {"key": "4", "title": "4"},
                        {"key": "5", "title": "5"}
                    ]},
                    {"key": "3", "title": "3", "children": [
                        {"key": "6", "title": "6"},
                        {"key": "7", "title": "7"}
                    ]}
                ]
            }
            tree_root = TreeOperate.from_dict(_data)
            tree_root.find("2").append(TreeOperate.from_dict({"key": "8", "title": "8"}))
            print(tree_root.find("8"))
            tree_root.find("8").remove()
            print(tree_root.find("8"))
            
        """
    
        def __init__(self, key=None):
            self.key = key
            self.pid = None
            self.data = {}
            self.parent = None  # type: Optional[TreeOperate]
            self.__children = []  # type: List[TreeOperate]
    
        def __str__(self):
            return str({
                "key": self.key,
                "pid": self.pid,
                "data": self.data,
                "children": self.__children
            })
    
        @staticmethod
        def from_dict(data):
            """
            
            从dict对象中返回TreeOperate对象
            :param data: dict
            :return: TreeOperate
            
            """
            tree = TreeOperate(data.get("key", None))
            for d in data:
                if d not in ["key", "children"]:
                    tree.data[d] = data[d]
            for i in data.get("children", []):
                tree.append(TreeOperate.from_dict(i))
            return tree
    
        @staticmethod
        def from_file(path):
            """
            
            从json文件中读取数据
            
            :param path: json文件路径 
            :return: TreeOperate
            
            """
            return TreeOperate.from_dict(require(path))
    
        @property
        def children(self):
            return self.__children
    
        def append(self, sub_tree: "TreeOperate"):
            """
            
            为当前节点添加子节点,节点类型必须是TreeOperate类型
            :param sub_tree: 子类型节点
            :return: None
            
            """
            if not isinstance(sub_tree, TreeOperate):
                raise TypeError("sub_tree must be of type TreeOperate")
            sub_tree.pid = self.key
            sub_tree.parent = self
            self.__children.append(sub_tree)
    
        def find(self, key: str):
            """
            
            根据key值查找节点
            :param key: key值
            :return: TreeOperate
            
            """
            if self.key == key:
                return self
            else:
                for i in self.__children:
                    result = i.find(key)
                    if result:
                        return result
            return None
    
        def remove(self, key=None):
            """
            
            删除节点,如果传递key值,将删除当前节点下匹配的子孙节点,
            如果不传递key值将当前节点从父节点中删除
            :param key: [可选] key值
            :return:
            
            """
            if key is None:
                if self.parent is not None:
                    self.parent.__children.remove(self)
            else:
                remove_child = self.find(key)
                if remove_child:
                    remove_child.parent.__children.remove(remove_child)
    
        def parse(self, callback: ParseCallable, deep=0):
            """
            
            遍历定制解析规则,返回解析内容
            
            :param callback: Callable[["TreeOperate", List[T], int], T] 解析回调函数返回解析结果
            :param deep: 当前解析深度,默认不需要填写,用于回调函数接收判断所在层级
            :return: 解析结果
            
            """
            child_parse_list = []
            for i in self.__children:
                child_parse_list.append(i.parse(callback, deep + 1))
            return callback(self, child_parse_list, deep)
    
        def to_dict(self, flat=False):
            """
            
            输出dict类型数据,用于json化
            :param flat: 是否将data参数内容直接映射到对象
            :return: dict
            
            """
            result = dict(key=self.key, pid=self.pid)
            if flat:
                for j in self.data:
                    result[j] = self.data[j]
            else:
                result["data"] = self.data
            children = []
            for i in self.__children:
                children.append(i.to_dict(flat))
            result["children"] = children
            return result
    
    
    def domain_parse_jsx(_tree_obj: TreeOperate):
        """
        
        保存额外解析数据,解析函数会被所有树叶节点对象调用,如果需要保存一些遍历叶子节点时的数据内容,
        可以定义一个函数,并增加一个变量用于接受。
        
        :param _tree_obj: 
        :return: 
        
        """
        data_pool = set()
    
        def parse_jsx(self: "TreeOperate", child: List[str], deep=0) -> str:
            p_str = ""
            props = self.data["props"]
            data_pool.add(self.data["title"])
            for p in props:
                if isinstance(props[p], str):
                    p_str += " {}={{\"{}\"}}".format(p, props[p])
                elif isinstance(props[p], bool):
                    p_str += " {}={{{}}}".format(p, str(props[p]).lower())
                elif isinstance(props[p], int):
                    p_str += " {}={{{}}}".format(p, props[p])
                else:
                    p_str += " {}={{\"{}\"}}".format(p, props[p])
            return "\n{0}<{1}{3}>{2}\n{0}</{1}>".format("\t" * deep, self.data["title"], "".join(child), p_str)
    
        result = tree_obj.parse(parse_jsx)
        return result, data_pool
    
    
    if __name__ == '__main__':
        tree_obj = TreeOperate.from_file("data.json")
        dom, import_pool = domain_parse_jsx(tree_obj)
        print(dom)
    

    相关文章

      网友评论

          本文标题:使用python3处理树形结构数据,tree data 树形数据

          本文链接:https://www.haomeiwen.com/subject/puyelktx.html