zoukankan      html  css  js  c++  java
  • 元类编程

    一. property动态属性

      @property:

        可以直接像属性那样访问,只是取数据(相当于get),可以加入自己的逻辑

      @name.setter:

       赋值(相当于set),对age
    from datetime import datetime,date
    class User():
        def __init__(self,name,birthday):
            self.name=name
            self.birthday=birthday
            self._age=0
        #函数的方式获取
        def get_age(self):
            return datetime.now().year-self.birthday.year
        #可以直接像属性那样访问,只是取数据(相当于get),可以加入自己的逻辑
        @property
        def age(self):
            return datetime.now().year-self.birthday.year
        #赋值(相当于set),对age
        @age.setter
        def age(self,value):
            self._age=value
    
    
    user=User('LYQ',date(year=1998,month=5,day=11))
    user.age=10
    print(user._age)
    print(user.age)


    二. __getattr__、__getattribute__魔法函数

      1.__getattr__:

        查找不到属性的时候调用

    class User():
        def __init__(self,name,birthday):
            self.name=name
            self.birthday=birthdaydef __getattr__(self, item):
            return 'not found attr'
           
    
    user=User('LYQ',date(year=1998,month=5,day=11))
    #查找不到age,进入__getattr__魔法函数
    print(user.age)

    class User():
        def __init__(self,info):
            self.info=info
    
        def __getattr__(self, item):
            return self.info[item]
    
    user=User(info={'name':'LYQ','gender':'man'})
    print(user.name)
    print(user.gender)

       2. __getattribute__比__getattr__更优先,无条件进入,不管能否找到(尽量不重写):

    class User():
        def __init__(self,name,info):
            self.name=name
            self.info=info
    
        def __getattr__(self, item):
            return self.info[item]
        #更优先,无条件进入,不管能否找到
        def __getattribute__(self, item):
            return '无敌了'
    
    user=User('LYQ',info={'name':'LYQ','gender':'man'})
    print(user.name)
    print(user.test)

    三. 属性描述符和属性查找过程

      1.前言:在类中,如果需要限制属性的类型

        如果用动态属性来验证(如要验证name和_age都是字符串),就需要写多个动态属性验证(代码重复度高,而且多)

      2.属性描述符:只需要实现(__get__,__set__,__delete__)中任意一个方法就是属性描述符

        2.1介绍:

          描述符的本质是新式类,并且被代理的类(即应用描述符的类)也是新式类。描述符的作用是用来代理一个类的属性,需要注意的是描述符不能定义在类的构造函数中,只能定义为类的属性,它只属于类的,不属于实例,我们通过查看实例和类的字典即可知晓。描述符是可以实现大部分Python类特性中最底层的数据结构的实现手段,我们常使用的@classmethod、@staticmethd、@property、甚至是__slots__等属性都是通过描述符来实现的。它是很多高级库和框架的重要工具之一,是使用到装饰器或者元类的大型框架中的一个非常重要组件。在一般的开发中我们可能用不到描述符,但是我们如果想要开发一个大型的框架或者大型的系统,那使用描述符会起到如虎添翼的作用。它的加盟将会使得系统更加完美。下面将简单的介绍描述符的使用。

        2.2分类:

          数据描述符:

            至少实现了内置属性__set__()和__get__()方法的描述符称为数据描述符;

          非数据描述符:

            实现了除__set__()以外的方法的描述符称为非数据描述符;

        2.3优先级:

          之所以要区分描述符的种类,主要是因为它在代理类属性时有着严格的优先级限制。例如当使用数据描述符时,因为数据描述符大于实例属性,所以当我们实例化一个类并使用该实例属性时,该实例属性已被数据描述符代理,此时我们对该实例属性的操作是对描述符的操作。描述符的优先级的高低如下:

          类属性 > 数据描述符 > 实例属性 > 非数据描述符 > 找不到的属性触发__getattr__()

        2.4例1:

    import numbers
    class IntField:
        #只需要实现以下任意一个方法就行(就能制定成属性描述符)
        def __get__(self, instance, owner):
            #保持和__set__中一样(value)
            return self.value
        def __set__(self, instance, value):
            if not isinstance(value,numbers.Integral):
                raise ValueError('int value need')
            if value<0:
                raise ValueError('positive value need')
            self.value=value
        def __delete__(self, instance):
            pass
    
    class User:
        age=IntField()
    
    if __name__=='__main__':
        user=User()
        #调用__set__验证成功
        user.age=6
        print(user.age)
        #字符串抛出错误
        user.age='abc'

      

      3.属性查找过程

    from datetime import date, datetime
    import numbers
    
    class IntField:
        #数据描述符
        def __get__(self, instance, owner):
            return self.value
        def __set__(self, instance, value):
            if not isinstance(value, numbers.Integral):
                raise ValueError("int value need")
            if value < 0:
                raise ValueError("positive value need")
            self.value = value
        def __delete__(self, instance):
            pass
    
    
    class NonDataIntField:
        #非数据属性描述符
        def __get__(self, instance, owner):
            return self.value
    
    class User:
        age = IntField()
        # age = NonDataIntField()
    
    '''
    如果user是某个类的实例,那么user.age(以及等价的getattr(user,’age’))
    首先调用__getattribute__。如果类定义了__getattr__方法,
    那么在__getattribute__抛出 AttributeError 的时候就会调用到__getattr__,
    而对于描述符(__get__)的调用,则是发生在__getattribute__内部的。
    user = User(), 那么user.age 顺序如下:
    
    (1)如果“age”是出现在User(类)或其基类的__dict__中, 且age是data descriptor, 那么调用其__get__方法, 否则
    
    (2)如果“age”出现在user(对象)的__dict__中, 那么直接返回 obj.__dict__[‘age’], 否则
    
    (3)如果“age”出现在User或其基类的__dict__中
    
    (3.1)如果age是non-data descriptor,那么调用其__get__方法, 否则
    
    (3.2)返回 __dict__[‘age’]
    
    (4)如果User有__getattr__方法,调用__getattr__方法,否则
    
    (5)抛出AttributeError
    
    '''

        3.1数据描述符,进入类User的__dict__中:

    import numbers
    
    class IntField:
        #数据描述符
        def __get__(self, instance, owner):
            return self.value
        def __set__(self, instance, value):
            if not isinstance(value, numbers.Integral):
                raise ValueError("int value need")
            if value < 0:
                raise ValueError("positive value need")
            self.value = value
        def __delete__(self, instance):
            pass
    
    
    class NonDataIntField:
        #非数据属性描述符
        def __get__(self, instance, owner):
            return self.value
    
    class User:
        age = IntField()
    
    if __name__ == "__main__":
        user = User()
        user.age=20
        #age是数据描述符,age没有进入到user的__dict__中,age是数据描述符,进入的是User类中的__dict__
        print(user.__dict__)
        print(getattr(user,'age'))
    '''
    输出:
    {}
    20
    '''

        3.2进入实例:非数据描述符

    import numbers
    
    class IntField:
        #数据描述符
        def __get__(self, instance, owner):
            return self.value
        def __set__(self, instance, value):
            if not isinstance(value, numbers.Integral):
                raise ValueError("int value need")
            if value < 0:
                raise ValueError("positive value need")
            self.value = value
        def __delete__(self, instance):
            pass
    
    
    class NonDataIntField:
        #非数据属性描述符
        def __get__(self, instance, owner):
            return self.value
    
    class User:
        age = NonDataIntField()
    
    if __name__ == "__main__":
        user = User()
        user.age=20
        #非数据描述符,age进入到user的__dict__中,age是数据描述符,进入的是User类中的__dict__
        print(user.__dict__)
        print(getattr(user,'age'))
    '''
    输出:
    {'age': 20}
    20
    '''

        3.3获取类中数据描述符值失败:

    import numbers
    
    class IntField:
        #数据描述符
        def __get__(self, instance, owner):
            return self.value
        def __set__(self, instance, value):
            if not isinstance(value, numbers.Integral):
                raise ValueError("int value need")
            if value < 0:
                raise ValueError("positive value need")
            self.value = value
        def __delete__(self, instance):
            pass
    
    
    class NonDataIntField:
        #非数据属性描述符
        def __get__(self, instance, owner):
            return self.value
    
    class User:
        age = IntField()
    
    if __name__ == "__main__":
        user = User()
       #直接对实例添加属性 user.
    __dict__['age']='haha' #用__dict__就和属性查找过程没有关系了,能找到 print(user.__dict__) #会抛错,age是数据属性描述符,首先进入类中调用属性,__get__返回value,但是没有这个值 print(user.age) ''' 输出: {'age': 'haha'} ...... AttributeError: 'IntField' object has no attribute 'value' '''

    四. __new__和__init__的区别(参考:区别)

     __new__传递的参数是类,是在生成对象的过程中调用,而__init__传递的是对象,是在生成对象后调用(__new__是在__init__之前的)

      注:

        1.__new__是用来控制对象的生成过程的,在对象生成之前;

        2.__init__是用来完善对象的;

        3.如果__new__不返回对象,则不会调用__init__方法;

        4.传递的参数需要在__init__中接收;   

        5.继承自object的新式类才有__new__

        6.__new__至少要有一个参数cls,代表当前类,此参数在实例化时由Python解释器自动识别

        7.__new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类(通过super(当前类名, cls))__new__出来的实例,或者直接是object的__new__出来的实例

        8.__init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值

        9.如果__new__创建的是当前类的实例,会自动调用__init__函数,通过return语句里面调用的__new__函数的第一个参数是cls来保证是当前类实例,如果是其他类的类名,;那么实际创建返回的就是其他类的实例,其实就不会调用当前类的__init__函数,也不会调用其他类的__init__函数

    五. 自定义元类

      1.通过函数动态创建类:

      

    def create_class(name):
        if name == 'user':
            class User:
                def __str__(self):
                    return 'user'
            return User
        elif name == 'company':
            class Company:
                def __str__(self):
                    return 'company'
            return Company
    
    MyClass = create_class('user')
    myclass = MyClass()
    print(myclass)
    '''
    输出:
    user
    '''

      2.通过type动态创建类:

        

    class BaseClass:
        def anwser(self):
            return '我是被继承的基类'
    def say(self):
        return '我是一个方法'
    #'User'表示类名,(BaseClass,)表示继承的类,()表示什么也没继承(object),{}中表示属性(name)和类中方法(say)
    User=type('User',(BaseClass,),{'name':'user','say':say})
    user=User()
    #调用基类中的方法
    print(user.anwser())
    #调用属性
    print(user.name)
    #调用方法
    print(user.say())
    '''
    输出:
    我是被继承的基类
    user
    我是一个方法
    '''

      3.什么是元类:

        元类就是创建类的类,对象<——class(对象)<——type.

        python中类的实例化过程,会首先寻找metaclass(首先找自己是否有metaclass,如果找不到,就会在继承的基类里面去找),通过metaclass去创建类,如果都找不到,就会通过type去创建类对象,实例

    参数和type中一样(类名,(),{})

    class MetaClass(type):
        def __new__(cls, *args, **kwargs):
            #元类中需要将__new__中的参数加进去
            return super().__new__(cls, *args, **kwargs)
    
    
    class User(metaclass=MetaClass):
        def __init__(self,name):
            self.name=name
        def __str__(self):
            return 'user'
        user=User(name="LYQ")
        print(user)
    '''
    输出:
    user
    '''

      注:

        可以在元类中做很多检查,如Iterable的元类AbcMeta等等

    六. 元类实现简单的orm

      1.简介django的model:

        dango的关系映射(ORM即model),继承于Model,Model的元类是ModelBase,在ModelBase中实现了一系列属性,然后注入到自己写的类中

       2.元类:

        第一种写法(__new__方法和type参数一样):

        第二种写法:

        

      3.实现orm源码:

      1 import numbers
      2 import pymysql
      3 #连接数据库
      4 conn=pymysql.connect(host='localhost',user='',password='',database='',charset='utf8')
      5 #获取游标
      6 cursor=conn.cursor()
      7 class Field:
      8     pass
      9 
     10 
     11 # 类似django的model
     12 class IntField(Field):
     13     # 注意参数顺序:在Python中定义函数,可以用必选参数、默认参数、可变参数和关键字参数,这4种参数都可以一起使用,或者只用其中某些,但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数和关键字参数。
     14     def __init__(self, db_column, min_value=None, max_value=None):
     15         self._value = None
     16         self.min_value = min_value
     17         self.max_value = max_value
     18         self.db_column = db_column
     19         if min_value is not None:
     20             if not isinstance(min_value, numbers.Integral):
     21                 raise ValueError('min_value must be int')
     22             if min_value < 0:
     23                 raise ValueError('min_value must be postive int')
     24         if max_value is not None:
     25             if not isinstance(max_value, numbers.Integral):
     26                 raise ValueError('max_value must be int')
     27             if min_value < 0:
     28                 raise ValueError('max_value must be postive int')
     29         if min_value is not None and max_value is not None:
     30             if min_value > max_value:
     31                 raise ValueError('min_value must be smaller than max_value')
     32 
     33     def __get__(self, instance, owner):
     34         return self._value
     35 
     36     def __set__(self, instance, value):
     37         if not isinstance(value, numbers.Integral):
     38             raise ValueError('int value need')
     39         if value > self.max_value or value < self.min_value:
     40             raise ValueError('value must between min_value and max_value')
     41         self._value = value
     42 
     43 
     44 class CharField(Field):
     45     def __init__(self, db_column, max_length):
     46         self._value = None
     47         self.db_column = db_column
     48         self.max_length = max_length
     49         if max_length is None:
     50             raise ValueError('you must spcify max_length for charfield')
     51 
     52     def __get__(self, instance, owner):
     53         return self._value
     54 
     55     def __set__(self, instance, value):
     56         if not isinstance(value, str):
     57             raise ValueError('string value need')
     58         if len(value) > self.max_length:
     59             raise ValueError('value len excess len of max_length')
     60         self._value = value
     61 
     62 
     63 # 元类
     64 class MetaClass(type):
     65     def __new__(cls, name, bases, attrs, **kwargs):
     66         # BaseModel的元类也是MetaClass,如果是BaseModel,则直接调用type的__new__方法
     67         if name == 'BaseModel':
     68             return super().__new__(cls, name, bases, attrs, **kwargs)
     69         fields = {}
     70         # 可以分别判断是否为IntField和CharField类型(这样写较麻烦),也可以让IntField和CharField同时继承Field,然后只需判断是否为Field类型
     71         for key, value in attrs.items():
     72             if isinstance(value, Field):
     73                 fields[key] = value
     74         # 获取Meta中的属性
     75         attrs_meta = attrs.get('Meta', None)
     76         _meta = {}
     77         db_table = name.lower()
     78         if attrs_meta is not None:
     79             # 如果有Meta,获取Meta中的db_name,如果有,就把表明将默认的类小写名替换
     80             table = getattr(attrs_meta, 'db_table', None)
     81             if table is not None:
     82                 db_table = table
     83         _meta['db_table'] = db_table
     84         attrs['_meta'] = _meta
     85         attrs['fields'] = fields
     86         del attrs['Meta']
     87         # 委托给父类type
     88         return super().__new__(cls, name, bases, attrs, **kwargs)
     89 
     90 
     91 class BaseModel(metaclass=MetaClass):
     92     # 专门处理参数
     93     def __init__(self, *args, **kwargs):
     94         for key, value in kwargs.items():
     95             setattr(self, key, value)
     96         return super().__init__()
     97 
     98     def save(self):
     99         fields=[]
    100         values=[]
    101         for key,value in self.fields.items():
    102             db_column=value.db_column
    103             if db_column is None:
    104                 db_column=key.lower()
    105             fields.append(db_column)
    106             value=getattr(self,key)
    107             values.append(str(value))
    108         #expected str instance, int found(抛错,使用join时,列表中必须是同一类型)
    109         sql="insert into {db_table}({fields}) value({values})".format(db_table=self._meta['db_table'],fields=','.join(fields),values=','.join(values))
    110         #执行sql语句
    111         cursor.execute(sql)
    112         conn.commit()
    113         cursor.close()
    114         conn.close()
    115 class User(BaseModel):
    116     # 写__init__有点麻烦,可以直接在写一个类处理参数,然后继承它,就不用每次都写__init__
    117     # def __init__(self):
    118     #     pass
    119     name = CharField(db_column='name', max_length=10)
    120     age = IntField(db_column='age', min_value=1, max_value=100)
    121 
    122     class Meta:
    123         db_table = 'user'
    124 
    125 if __name__ == '__main__':
    126 
    127     user = User(name='haha', age=20)
    128     user.save()
    View Code
  • 相关阅读:
    大数据架构资料
    SQLServer 随机生成指定范围的日期
    源码解析Django CBV的本质
    源码剖析Django REST framework的认证方式及自定义认证
    Django----中间件详解
    权限管理系统
    Django---分页器、中间件
    linux每日命令(37):top命令
    linux每日命令(36):wc命令
    linux每日命令(35):grep命令
  • 原文地址:https://www.cnblogs.com/lyq-biu/p/10427008.html
Copyright © 2011-2022 走看看