首页 > 编程语言 >自己写一个 NODE/ATTR 的结构

自己写一个 NODE/ATTR 的结构

时间:2023-05-21 17:37:15浏览次数:30  
标签:NODE __ ATTR self field dict ._ 结构 name

## python 3.8 以上
from typing import Dict, List, TypeVar, Tuple, Generic, get_args

import json

T = TypeVar("T")


# 数据的默认值


def get_dft(tp):
    if issubclass(tp, str):
        return ""
    elif issubclass(tp, int):
        return 0
    elif issubclass(tp, float):
        return 0.0
    elif hasattr(tp, "_user_default_"):  # 如果是自定类, 实现了这个属性, 也可以用
        return getattr(tp, "_user_default_")
    else:
        return None


# 字段的实现, 也做了类型检查,但现在只能是基本类
class base_field(Generic[T]):
    """字段基类"""

    name: str
    tp: type  # 用于类型检查

    def __init__(self, default=None, show=True):
        self.name = ""
        self.tp = None  # 初始化时,类型是没有设定的,这是因为 无法获取到 T 的具体值.
        self._default = default  # 默认值
        self.is_show = show  # to dict 时, 是否显示出来

    def __get__(self, instance, owner) -> T:
        """从 instance 的 __field_data__ 里取值, 并返回"""

        value_con = getattr(instance, "__field_data__", None)
        if value_con is None:
            setattr(instance, "__field_data__", {})
            value_con = getattr(instance, "__field_data__")
        dd = value_con.get(self.name, None)

        # 当取值是NONE 时, 试着获取默认值
        if dd is None and not instance.__field_default__[self.name]:
            dd = self._default
        if dd is None:
            print(
                f":: WARN :: value is None : {instance.__class__.__name__}(). {self.name}"
            )
        return dd

    def __set__(self, instance, value):
        """往 instance 的 __field_data__ 里填值"""
        value_con = getattr(instance, "__field_data__", None)
        if value_con is None:
            setattr(instance, "__field_data__", {})
            value_con = getattr(instance, "__field_data__")

        if isinstance(value, self.tp):
            value_con[self.name] = value
        else:
            raise TypeError(f"应该填入 {self.tp},实填{type(value)}:{str(value)[:30]}")
        if not instance.__field_default__[self.name]:
            if self._default is None:
                self._default = get_dft(self.tp)
            instance.__field_default__[self.name] = True

    def __set_name__(self, owner, name):
        """在类初始化时调用这个函数,\n
        把自己放到类的 __field_setting__ 里去,\n
        获取到具体的 T 的类型, 把它绑定到 self.tp 上.
        """
        assert self.name == "", f"-> 字段已经有名称,不能重复命名 :now <{self.name}> , {name} "
        assert str(name).startswith("a_")  # 字段强制 用 a_ 开头
        self.name = name
        __field_setting__ = getattr(owner, "__field_setting__", None)
        if __field_setting__ is None:
            setattr(owner, "__field_setting__", {})
            __field_setting__ = getattr(owner, "__field_setting__")
        ff = __field_setting__.get(name, None)
        if ff is not None:
            raise NameError(f"只能有一个同名字段.{name}")
        else:
            self.name = name
            __field_setting__[name] = self
            self.tp = get_args(self.__orig_class__)[0]  # ##


class Attr(base_field[T]):
    """
    Attr(default=None, show=True) \n
    用字段 实现 属性定义
    """


class node_meta(type):
    """
    元类 用于控制 NODE 初始化时的一些动作
    """

    _class_list = {}
    _fn_temp = lambda: print("没找到函数名称")

    def __new__(cls, name, bases, attrs):
        # 初始化时, 把自己的  __field_setting__ 与父类的 __field_setting__ 隔离开
        if name == "node_base":
            fs = {}
        else:
            fs = {}
            for i in bases:
                for n, fds in i.__field_setting__.items():
                    fs[n] = fds
        attrs["__field_setting__"] = fs

        # --

        # 生成类
        obj_tp = super().__new__(cls, name, bases, attrs)

        node_meta._class_list[name] = obj_tp

        # 初始化默认值的读取标志
        if getattr(obj_tp, "__field_default__", None) is None:
            setattr(obj_tp, "__field_default__", {})

        for ff, vv in attrs.items():
            if ff.startswith("a_"):
                obj_tp.__field_default__[ff] = False

        # 执行子类初始化钩子
        getattr(obj_tp, "__on_sub_class__", cls._fn_temp)()

        return obj_tp


class node_base(metaclass=node_meta):
    """NODE 的 基类"""

    @classmethod
    def __on_sub_class__(cls):
        """子类钩子"""
        pass

    @classmethod
    def __all_sub_classes__(cls):
        return node_meta._class_list

    def to_dict(self, all_show=False):
        """导出 dict形式,用于传输"""
        raise NotImplementedError

    @classmethod
    def from_dict(cls, d: "List") -> "node_base":
        """导入 dict形式,用于传输"""
        assert isinstance(d, List)
        assert isinstance(d[0], Dict)

        t1: Dict = d[0]
        assert t1.get("_node_type_", None) is not None
        nd: type = node_meta._class_list[t1["_node_type_"]]
        return nd.from_dict(d)

    def __str__(self):
        return str(self.to_dict())

    __repr__ = __str__


class Node_desc(node_base):
    """这是一种描述用的类,不可以添加子结点"""

    a_count = Attr[int](0)

    def to_dict(self, all_show=False):
        r = [
            {
                "_node_type_": self.__class__.__name__,
                "_attrs": {
                    k: getattr(self, k)
                    for k, v in self.__field_setting__.items()
                    if all_show or v.is_show
                },
            }
        ]
        return r

    @classmethod
    def from_dict(cls, d: List) -> node_base:
        assert isinstance(d, List)
        assert isinstance(d[0], Dict)

        t1: Dict = d[0]
        assert t1.get("_node_type_", None) is not None
        ndtp: type = node_meta._class_list[t1["_node_type_"]]
        if issubclass(ndtp, cls):
            ret = ndtp()
            atrs = t1.get("_attrs", {})
            for k, v in atrs.items():
                setattr(ret, k, v)
            return ret
        raise TypeError("NODE DESC 数据格式不对")


class Node(node_base):
    """常规结点,有属性,  他可以添加子结点, 并为子结点指定一个描述符,记录不同的状态"""

    desc_type = Node_desc
    a_name = Attr[str]("")

    def __init__(self) -> None:
        self.__field_default__ = {}
        for i, v in self.__class__.__field_default__.items():
            self.__field_default__[i] = False

        self.__data: List["Node"] = []
        self._desc_dict: Dict[str, Node_desc] = {}
        self._names_of_child: List[str] = []

    def __getitem__(self, key) -> "Node":
        return self.__data[key]

    def __setitem__(self, key, v: "Node"):
        assert isinstance(v, Node)
        self.__data[key] = v
        if v.a_name in self._names_of_child:
            self._desc_dict[v.a_name] = self.desc_type()
            self._desc_dict[v.a_name].a_count = 1
        self._names_of_child = [i.a_name for i in self.__data]

    def append(self, item: "Node", desc: Node_desc = None):
        if desc is None:
            assert isinstance(item, Node)
            if item.a_name in self._names_of_child:
                self._desc_dict[item.a_name].a_count += 1
            else:
                self.__data.append(item)
                self._desc_dict[item.a_name] = self.desc_type()
                self._desc_dict[item.a_name].a_count = 1
            self._names_of_child = [i.a_name for i in self.__data]
        else:
            assert isinstance(item, Node)
            assert item.a_name not in self._names_of_child
            assert isinstance(desc, self.desc_type)
            self.__data.append(item)
            self._desc_dict[item.a_name] = desc
            self._names_of_child = [i.a_name for i in self.__data]

    def remove(self, item: "Node"):
        assert isinstance(item, Node)
        if item.a_name in self._names_of_child:
            self._desc_dict.pop(item.a_name)
            for i in self.__data:
                if i.a_name == item.a_name:
                    self.__data.remove(i)
                    break

    def index(self, item: "Node"):
        assert isinstance(item, Node)
        r = 0
        for i in self.__data:
            if i.a_name == item.a_name:
                return i
            r += 1
        return -1

    def pop(self, index: "Node | int" = -1):
        if isinstance(index, int):
            nod = self[index]
        elif isinstance(index, Node):
            nod = self[self.index(index)]
        cc = self._desc_dict[nod.a_name].a_count
        if cc == 1:
            self.remove(nod)
            return nod
        elif cc > 1:
            self._desc_dict[nod.a_name].a_count -= 1
            return nod
        else:
            raise ValueError(f"无法 POP ,数量过少. <{self.a_name}>")

    def __len__(self):
        return len(self.__data)

    def to_dict(self, all_show=False):
        rr = [
            {
                "_node_type_": self.__class__.__name__,
                "_attrs": {
                    k: getattr(self, k)
                    for k, v in self.__field_setting__.items()
                    if all_show or v.is_show
                },
            }
        ]

        for i in self.__data:
            rr.append(
                [self._desc_dict[i.a_name].to_dict(all_show), i.to_dict(all_show)]
            )

        return rr

    @classmethod
    def from_dict(cls, d: "List"):
        assert isinstance(d, List)
        assert isinstance(d[0], Dict)

        t1: Dict = d[0]
        assert t1.get("_node_type_", None) is not None
        ndtp = node_meta._class_list[t1["_node_type_"]]

        if issubclass(ndtp, Node):
            ret = ndtp()
            atrs = t1.get("_attrs", {})
            for k, v in atrs.items():
                setattr(ret, k, v)
            children = d[1:]
            dsc_tp = ndtp.desc_type
            for c in children:
                dsc = dsc_tp.from_dict(c[0])
                # print(c[1])
                c_chd = Node.from_dict(c[1])
                ret.append(c_chd, dsc)
            return ret
        else:
            raise TypeError("NODE 数据格式不对")


def node_from_dict(data):
    return node_base.from_dict(data)


def node_to_json(n: Node):
    rr = n.to_dict(True)

    return json.dumps(rr, ensure_ascii=False)


def node_from_json(data: str):
    return node_from_dict(json.loads(data))


if __name__ == "__main__":

    class A(Node):
        a_width = Attr[int](50)

    class B(Node):
        a_deep = Attr[int](65)

    class AA(A):
        a_height = Attr[int](15, False)

    a = A()
    aa = AA()
    c = A()
    print(aa.a_width)

    a.a_name = "a_1"
    aa.a_name = "a_2"
    c.a_name = "a_3"
    a.a_width = 30
    aa.a_width = 20
    aa.a_height = 40

    a.append(c)
    aa.append(a)

    xx = aa.to_dict(True)

    print(xx)

    yy = node_base.from_dict(xx)
    print(yy.to_dict(True))

标签:NODE,__,ATTR,self,field,dict,._,结构,name
From: https://www.cnblogs.com/unm001/p/17418842.html

相关文章

  • Node & NPM upgrade
     Node和Npm升级节奏都非常快,因此版本升级是Noder经常回碰到的事情.NPMnpm升级非常方便,直接使用npm就可以1$npmupdate-gnpmnpm卸载方法如下1$sudonpmuninstallnpm-g如果该方法失败,可以先获取npm源代码,然后$sudomakeuninstallNode关于Node的安装之前......
  • 把一个nodejs程序做成传统linux服务
     1:http://howtonode.org/deploying-node-upstart-monit  (  DeployingNode.jsWithUpstartandMonit)  2: https://www.exratione.com/2013/02/nodejs-and-forever-as-a-service-simple-upstart-and-init-scripts-for-ubuntu/    ( Node.jsandFor......
  • Nodejs(七)-跨域访问
     今天在使用ajax(http://localhost:8080%EF%BC%89%E8%B0%83%E7%94%A8nodejs%E5%86%99%E7%9A%84restfulAPI(http://localhost:3000%EF%BC%89%E6%97%B6%EF%BC%8C%E6%8A%A5%E4%BA%86%E5%A6%82%E4%B8%8B%E4%B8%80%E4%B8%AA%E9%94%99%E8%AF%AF%EF%BC%9AXMLHttpRequestcannotloadhttp:......
  • Fibers and Threads in node.js – what for?
    https://bjouhier.wordpress.com/2012/03/11/fibers-and-threads-in-node-js-what-for/ Ilike node.js,andI’mnottheonlyone,obviously!Ilikeitprimarilyfortwothings:itissimple anditis veryfast.Ialreadysaiditmanytimesbutonemorewon’thu......
  • nodejs多线程,真正的非阻塞
    干货:收藏:http://cnodejs.org/topic/518b679763e9f8a5424406e9node从他推出至今,充满赞美和饱受诟病的都是其单线程模型,所有的任务都在一个线程中完成(I/O等例外),优势的地方自然是免去了频繁切换线程的开销,以及减少资源互抢的问题等等,但是当nodejs面对cpu密集型模型的时候就力不从心了......
  • [nodejs]国内npm安装nodejs modules失败的几个解决方案
    使用npm安装node模块时经常有卡住安装失败的情况,如图所示。原因在于npm服务器在美国,还有就是某强大的防火墙作用。这样的问题导致很多新手放弃使用node,几乎每天都有新手再问这个问题。现在分享一下解决这个问题的解决方案1.可以通过一劳永逸的方式修改代理地址2.更换npm源拼rp (......
  • centos7目录结构
    刚安装好的CentOS系统中,主要的目录如下:-`/bin`—存放着最常用的的可执行二进制文件。-`/sbin`—存放着系统管理员使用的可执行二进制文件。-`/boot`—存放系统启动所需的文件,例如内核和BootLoader等。-`/dev`—存放着设备文件,例如磁盘、输入设备和网络接口......
  • org.apache.jasper.JasperException: /pages/role-list.jsp (行.: [145], 列: [8]) 根
    org.apache.jasper.JasperException:/pages/role-list.jsp(行.:[145],列:[8])根据标记文件中的TLD或attribute指令,attribute[items]不接受任何表达式 web.xml中版本号不兼容产生的问题;解决方法:<%@taglibprefix=“c”uri=“http://java.sun.com/jstl/core”%>改为<%@t......
  • java 递归拼接树形结构代码
    1、json数据(目标)[{label:'一级1',children:[{label:'二级1-1',children:[{label:'三级1-1-1'}]}]},{label:'一级2'......
  • 在Study.BlazorOne项目中引入Study.Trade模块的实体的表结构
    #1、修改EntityFrameworkCore项目下的BlazorOneDbContext文件,增加一行代码即可增加Study.Trade.EntityFrameworkCore中的这个方法:如无意外,文件顶部会自动引用命名空间:#2、生成迁移文件执行这条命令:dotnetefmigrationsaddAddTradeModule效果如下:#3、将数据库的改动,应用到数......