zoukankan      html  css  js  c++  java
  • 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__)
    
  • 相关阅读:
    CF1051F The Shortest Statement 题解
    CF819B Mister B and PR Shifts 题解
    HDU3686 Traffic Real Time Query System 题解
    HDU 5969 最大的位或 题解
    P3295 萌萌哒 题解
    BZOJ1854 连续攻击游戏 题解
    使用Python编写的对拍程序
    CF796C Bank Hacking 题解
    BZOJ2200 道路与航线 题解
    USACO07NOV Cow Relays G 题解
  • 原文地址:https://www.cnblogs.com/2222bai/p/11794913.html
Copyright © 2011-2022 走看看