• Day 38 元类/ORM


    元类

    什么是元类

    在Python中一切皆对象,那么我们用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类,即元类可以简称为类的类

    元类(type)---->类---->实例对象

    为什么用元类

    元类是负责产生类,所以我们学习元类或者自定义元类的目的:是为了控制类的产生过程,还可以控制对象的产生过程

    创建类的两种方式

    普通的创建类

    class Test:
    	def __init__(self, num, num2):
    		self.num = num
    		self.num2 = num2
    

    用元类(type)创建类

    类 = type(类名, 由父类名称组成的元组(针对继承的情况,可以为空), 包含属性的字典(名称:值))

    Test2 = type('Test2', (), {'num': 100, 'num2': 200})
    

    __call__

    '''
    在python中,一切皆对象。
    '''
    
    
    # 自定义元类
    class MyMeta(type):
        # 子类的方法与父类的方法一样,先用子类的,子类覆盖父类的__init__方法。
        # 控制了类的定义
        def __init__(self, class_name, class_base, class_dict):
            # print(class_name)
    
            # 判断字符串首字母是否大写
            if not class_name.istitle():
                raise TypeError('类的首字母必须大写!')
    
            # 控制类中必须要有注释
            if not class_dict.get('__doc__'):
                raise TypeError('类内部必须要写注释!')
    
            # print(class_base)
            # print(class_dict)
            super().__init__(class_name, class_base, class_dict)
    
        # 模拟type元类内部做的事情
        # 元类触发的__call__可以控制类的调用。调用__call__会触发以下两点
        def __call__(self, *args, **kwargs):
            # 1.会调用__new__()--> obj, 会创建一个空对象。
            obj = object.__new__(self)
            # 2.会执行__init__(obj, *args, **kwargs),
            obj.__init__(*args, **kwargs)
            return obj
    
        # 可以通过元类内部的__new__控制对象的创建
    #    def __new__(cls, *args, **kwargs):
    #        pass
    
    
    class Bar:
        pass
    
    
    # metaclass ---> 自定义的元类
    # 因为Foo类继承了元类,必须手动继承object
    class Foo(Bar, metaclass=MyMeta):  # MyMeta(Foo, Foo_name, (Bar, ), foo_dict)
        '''
        这是一个Foo类
        '''
        # 摊开坦克
        x = 10
    
        def __init__(self, y, z):
            self.y = y
            self.z = z
    
        def f1(self):
            print('from Foo.f1...')
    
    
    foo = Foo(20, 30)  # 调用Foo对象,会触发__call__
    

    ORM(对象关系映射)

    对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式

    '''
    ORM: 对象关系映射 ---> 映射到数据库MySQL中的数据表
    
    类名 ---> 表名
    对象 ---> 一条记录
    对象.属性 ---> 字段
    
    模拟Django的ORM,为了,将数据库的 增、删、改、查,全部封装成
    一个个的方式,比如: save, delete, update, select。
    '''
    
    
    '''
    问题1: 解决代码荣誉问题,比如有100张表,需要写100次__init__。
    解决1: 继承一个父类,父类中定义一个__init__。
    
    问题2: 无法预测每一张表中的字段是什么,无法通过父类的__init__解决问题。
    解决2: 通过继承字典,内部的__init__, 可以接受“任意个数”的“关键字参数”。
    
    问题3: 继承字典的类实例化的对象,无法通过“对象.属性”的方式存取值。
    解决3: 通过__setattr__,__getattr__来实现,让字典对象与普通对象一模一样,并且具备字典原有的特性。
        
    '''
    
    
    '''
    元类需要处理的问题:
        1.给数据表类,强制必须要有一个主键。
        2.主键必须是唯一的。
        3.
    '''
    
    # user_obj = User(user_id=1, user_name='tank', age=18)
    # # insert into User(name, age) values('tank', 18);
    # user_obj.save()
    #
    # list1 = []
    #
    # obj.save()  # obj.table_name,
    # sql = 'insert into Movie(name, age, 3, 4, 5, ...) values('tank', 18, 3, 4, 5)'
    # # insert into obj.table_name(name, age, 3, 4, 5, ...) values('tank', 18, 3, 4, 5);
    
    
    # 1.创建字段的类型, 对应数据表中的一个个字段的创建规范
    class Field:
        def __init__(self, name, column_type, primary_key, default):
            self.name = name
            self.column_type = column_type
            self.primary_key = primary_key
            self.default = default
    
    
    # Integer
    class IntegerField(Field):
        def __init__(self, name, column_type='int', primary_key=False, default=0):
            super().__init__(name, column_type, primary_key, default)
    
    
    # String
    class StringField(Field):
        def __init__(self, name, column_type='varchar(64)', primary_key=False, default=None):
            super().__init__(name, column_type, primary_key, default)
    
    
    '''
    元类需要处理的问题:
        1.一张表必须要有一个表名(table_name)。
        2.给数据表类,强制必须要有一个主键,主键必须是唯一的。
        3.将数据表中,所有的字段对象,都存放在一个独立的字典中(mappings)
            存不是目的,目的是为了取值方便。
    '''
    
    
    class OrmMetaClass(type):
        # def __new__(cls, *args, **kwargs):
        # print(args, 'args............')
        # print(kwargs, 'kwargs........')
    
        # OrmMetaClass(class, class_name, class_base, class_dict)
    
        def __new__(cls, class_name, class_base, class_dict):
            # print(class_name, '类名--》表名')
            # print(class_base, '基类/父类')
    #        print(class_dict, '类的名称空间')
    
            # 过滤Models类
            if class_name == 'Models':
                # models类中,什么都不做,将类原路返回。
                return type.__new__(cls, class_name, class_base, class_dict)
    
            # 1.一张表必须要有表名
            # 假如table_name没有值,则将类名当做表名
            table_name = class_dict.get('table_name', class_name)  # get--> self.table_name
            
            # 2.主键名
            primary_key = None
    
            # 3.定义一个空字典, 专门用来存放字段对象
            mappings = {}
    
            # 遍历名称空间中所有的属性
            for key, value in class_dict.items():
    #            print(key, value)
                # 除了有字段,还有其他字段以外的属性
                # 过滤字段对象以外的内容
                if isinstance(value, Field):
                    mappings[key] = value
    
                    # 判断字段对象primary_key是否为True
                    if value.primary_key:
                        # 先判断初识的primary_key是否有值
                        # 判断主键是否已存在
                        if primary_key:
                            raise TypeError('只能有一个主键!')
    
                        # 若主键不存在,则给primary_key赋值
                        primary_key = value.name
    
            # 节省资源: 因为mappings与原类中名称空间中的属性重复,为了节省内存,剔除重复的属性。
            for key in mappings.keys():
                class_dict.pop(key)
    
            # 判断是否有主键
            if not primary_key:
                raise TypeError('必须有一个主键')
    
            # 给类的名称空间添加表名
            class_dict['table_name'] = table_name
            # 给类的名称空间添加主键名
            class_dict['primary_key'] = primary_key
            # 给类的名称空间添加一个mappings字典,字典中拥有所有字段属性
            class_dict['mappings'] = mappings
            return type.__new__(cls, class_name, class_base, class_dict)
    
    
    class Models(metaclass=OrmMetaClass):  # OrmMetaClass(Models, Models_name, base, class_dict)
    #    def __getattr__(self, item):
    ##        print(item, '调用没有的属性时会触发...')
    #        # 将字典的值,返回
    #        return self.get(item)
    #
    #    def __setattr__(self, key, value):
    #        print(key, value)
    #        self[key] = value
    #    
        def __init__(self, **kwargs):
            for name, values in kwargs.items():
                setattr(self, name, values)
                
        def save(self):
            fields = []
            args = []
            for key, value in self.mappings.items():
    #            print(key, getattr(self, key, None))
                fields.append(key)
                args.append(getattr(self, key, None))
            sql = 'insert into %s(%s) values (%s)' % (self.table_name, ','.join(fields), ','.join(str(i) for i in args))     
            print(sql)    
    # 用户表类
    class User(Models):  # ---> 表名
        # table_name = 'user_info'
        # 强调: 最好与字段类型的name属性同名
        user_id = IntegerField(name='user_id', primary_key=True)
        user_name = StringField(name='name')
        pwd = StringField(name='pwd')
        pass
    
    
    # 用户表类
    class Movie(Models):  # ---> 表名
        # table_name = 'user_info'
        # 强调: 最好与字段类型的name属性同名
        movie_id = IntegerField(name='user_id', primary_key=True)
        movie_name = StringField(name='name')
        pass
    
    user = User(user_id=1, user_name='tiny', pwd=123)
    user.save()
    
    
    #if __name__ == '__main__':
    #    print(User.__dict__)
    
  • 相关阅读:
    set用法
    01分数规划
    unique && stl的全排列
    lower_bound() && upper_bound()
    spfa判负环
    倍增求LCA
    数据生成c++程序模板
    samba
    vsftp快速配置
    grub丢失的修复
  • 原文地址:https://www.cnblogs.com/2222bai/p/11794913.html
Copyright © 2020-2023  润新知