元类编程
一. 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()