zoukankan      html  css  js  c++  java
  • 元类、orm

    一、内置函数exec

    作用:

    在全局变量和局部变量上下文中执行给定的源。源可以是表示一个或多个Python语句的字符串,也可以是compile()返回的代码对象。全局变量必须是字典,局部变量可以是任何映射,默认为当前全局变量和局部变量。如果只提供全局变量,则局部变量默认为全局变量。

    语法:

    # 局部名称空间
    # 1.文本形式的python代码
    code = '''
    x = 100
    y = 200
    def func():
        pass
    '''
    
    # 2.全名的名称空间 {}
    global_dict = {}
    
    # 3.局部名称空间 {}
    local_dict = {}
    
    exec(code, global_dict, local_dict)
    
    # print(global_dict)
    
    print(local_dict)
    

    二、元类

    1. 什么是元类

    我们都知道在python中一切皆对象,其实python中的类也是一种类对象。既然是对象,那肯定是实例化出来的。因此,我们把可以实例化出类的类叫做元类。在Python中,元类即为type,type是所有类对象的类。

    2. 元类的作用

    元类可以帮我们控制类的创造。

    3. 创建类的两种方法

    1. 通过class关键字创建类,内部会自动调用type(),type帮我们创建一个自定义类。
    2. 通过手动调用type()实例化得到自定义的类。
    # 方法1
    class Chinese:
    
        country = 'China'
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def speak(self):
            print('speak Chinese...')
    
    
    p1 = Chinese('tank', 18)
    print(Chinese)
    print(p1)
    print(type(p1))  # Chinese
    print(type(Chinese))  # <class 'type'>
    
    
    # 方法2
    class_name = 'Chinese'
    class_base = (object, )
    class_dict = {}
    code = '''
    country = "China"
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def speak(self):
        print("speak Chinese...")
    '''
    exec(code, {}, class_dict)
    Chinese = type(class_name, class_base, class_dict)
    print(Chinese)
    

    4. 怎么自定义创建元类

    1. 自定义一个元类,需要继承type,可以派生出自己的属性与方法。

    2. 如果想要给类指定元类,可以使用metaclass指定一个自定义元类。

    # 语法:
    class func(metaclass=自定义元类):
    
    
    # 自定义一个子元类
    class MyMeta(type):
        # 子类的方法与父类的方法一样,先用子类的,子类覆盖父类的__init__方法。
        # 控制了类的定义
        def __init__(self, class_name, class_base, class_dict):
            # class_name为MyMeta实例化出来的对象的对象名,
            # class_base为MyMeta实例化出来的对象的父类名,
            # class_dict为MyMeta实例化出来的对象的名称空间。
            print(class_name)
            print(class_base)
            print(class_dict)
            
            super().__init__(class_name, class_base, class_dict)
    
    # metaclass ---> 自定义的元类
    # 因为Foo类只是由元类实例化出来的一个类对象,它还可以继承一个父类object
    class Foo(object,metaclass=MyMeta):  # MyMeta(Foo,(object, ),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,意为对象关系映射,可以将一个对象映射到MySQL中的数据表,使类和表一一对应。

    类名 ---> 表名

    一个对象 ---> 一条记录

    对象.属性 ---> 字段

    1. ORM中可能会遇到的问题

    1. 问题1: 解决代码荣誉问题,比如有100张表,需要写100次__init__。
    • 解决1: 继承一个父类,父类中定义一个__init__。
    1. 问题2: 无法预测每一张表中的字段是什么,无法通过父类的__init__解决问题。
    • 解决2: 通过继承字典,内部的__init__, 可以接受“任意个数”的“关键字参数”。
    1. 问题3: 继承字典的类实例化的对象,无法通过“对象.属性”的方式存取值。
    • 解决3: 通过__setattr__,__getattr__来实现,让字典对象与普通对象一模一样,并且具备字典原有的特性。

    2. ORM中元类需要解决的问题

    1. 一张表必须要有一个表名

    2. 给数据表类,强制必须要有一个主键,而且主键必须是唯一的。

    3. 将数据表中 所有的字段对象,都存放在一个独立的字典中。

      (存不是目的,目的是为了取值方便)

    # 创建字段的类型,对应数据表中的一个个字段的创建规范
    class Field:
    
        # 每个字段都会有字段名,列类型,是否为主键,默认值 这些属性
        def __init__(self,field_name,column_type,primary_key,default):
            self.field_name = field_name
            self.column_type = column_type
            self.primary_key = primary_key
            self.default = default
    
    # 整形integer字段
    class IntegerField(Field):
        def __init__(self,field_name,column_type='int',primary_key=False,default=0):
            super().__init__(field_name,column_type,primary_key,default)
    
    # 字符串string字段
    class StringField(Field):
        def __init__(self,field_name,column_type='varchar(32)',primary_key=False,default=''):
            super().__init__(field_name,column_type,primary_key,default)
    
    
    # 设置自定义元类,满足定义类时的一些需求
    class OrmMetaClass(type):
    
    
        def __new__(cls, class_name, class_base,class_dict):
            print(1,class_dict)
    
            # 过滤Models类
            if class_name == 'Models':
                return type.__new__(cls, class_name, class_base,class_dict)
    
            # 表名
            table_name = class_dict.get('table_name',class_name)
            # 主键名
            primary_key = None
            # 定义一个空字典,专门用来存放字段对象
            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值是否为true
                        # 判断主键是否已存在
                        if primary_key:
                            raise TypeError('只能有一个主键!')
    
                        # 若主键不存在,则给初始的primary_key赋值
                        primary_key = value.field_name
    
            # 判断是否有一个主键
            if not primary_key:
                raise TypeError('请设置一个主键!')
    
            print(2, class_dict)
            # 给类的名称空间添加表名
            class_dict['table_name'] = table_name
            # 给类的名称空间添加主键名
            class_dict['primary_key'] = primary_key
            # 给类的名称空间添加mappings字典
            class_dict['mappings'] = mappings
    
            # 资源节省,剔除名称空间内重复的属性
            for key in mappings.keys():
                class_dict.pop(key)
    
            print(3, class_dict)
            # 元类限制结束,返回一个类
            return type.__new__(cls, class_name, class_base,class_dict)
    
    
    # 无法预测每一张表中有哪些字段,通过继承字典,可以接受“任意个数”的“关键字参数”
    class Models(dict,metaclass=OrmMetaClass):
    
        # 在通过 对象.属性 试图获取一个不存在的属性时触发
        def __getattr__(self, item):
            # 正常情况下,字典对象.属性 是无法取得一个值的(none),
            # 但是通过__getattr__方法可以使 对象.属性 返回一个值
            # 从而让字典对象看起来像一个普通的对象,可以用 对象.属性 的方法取值
            return self.get(item)
    
        # 在进行设置属性(赋值:对象.属性 = 属性)操作时触发
        def __setattr__(self, key, value):
            # 因为self(Models)是一个字典,所以可以用下面的方式进行添加值
            # 从而可以使一个字典对象可以通过 对象.属性=属性值 的方式赋值
            self[key] = value
    
    
    # 用户表(一个类就是一张表)
    class User(Models):
        # 每个属性就是一个字段
        user_id = IntegerField(field_name='user_id',primary_key=True)
        user_name = StringField(field_name='user_name')
        user_pwd = StringField(field_name='pwd')
    
    # 电影表
    class Movie(Models):
        movie_id = IntegerField(field_name='movie_id',primary_key=True)
        movie_name = StringField(field_name='movie_name')
    
    
    if __name__ == '__main__':
        print(4,User.__dict__)
    
  • 相关阅读:
    tryparse的用法,^0*[1-9]d*$
    寻找指定的进程然后杀死的代码写法
    P2421 [NOI2002]荒岛野人
    P2568 GCD
    P1445 [Violet]樱花
    P3119 [USACO15JAN]草鉴定Grass Cownoisseur
    P1314 聪明的质监员
    P3811 【模板】乘法逆元
    P3943 星空
    P3225 [HNOI2012]矿场搭建
  • 原文地址:https://www.cnblogs.com/bowendown/p/11795203.html
Copyright © 2011-2022 走看看