元类编程

一. 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
原文地址:https://www.cnblogs.com/lyq-biu/p/10427008.html