前言
描述符是用于代理另一个类的属性,一般用于大型的框架中,在实际的开发项目中较少使用,本质是一个实现了__get__(),__set__(),__delete__()其中一个方法的新式类
__get__():调用一个属性时执行
__set__():属性赋值时执行
__delete__():采用del删除属性时触发
示例
#描述符 class test: def __get__(self, instance, owner): print('---get方法') def __set__(self, instance, value): print('---set方法') def __delete__(self, instance): print('---delete方法') class test2: name = test()#描述符代理了test2类里的name属性 t1 = test2() #调用了被描述符代理的属性,执行__get__()方法 t1.name #---get方法 #赋值了被描述符代理的属性,执行__set__()方法 t1.name = 1 #---set方法 #赋值了被描述符代理的属性,执行__delete__()方法 del t1.name #---delete方法
属性查找的优先级
1.类属性
2.数据描述符(同时实现了__get__()和__set__()方法)
3.实例属性
4.非数据描述符(没有实现__set__()方法)
5.找不到的属性执行__getattr__()
#非数据描述符 class test: def __get__(self, instance, owner): print('get方法') class test2: name = test()#描述符代理了test2类里的name属性 t1 = test2() #在实例对象里中创建了name属性,并赋值chen t1.name = 'chen' print(t1.__dict__) #{'name': 'chen'}
利用描述符实现赋值类型限制(一)
#数据描述符 class Typed: def __init__(self,key,value_type): self.key = key self.value_type = value_type def __get__(self, instance, owner): print('get方法') return instance.__dict__[self.key] def __set__(self, instance, value): print('set方法') if not isinstance(value,self.value_type): #判断类型 raise TypeError('传入的类型不是',self.value_type) instance.__dict__[self.key] = value def __delete__(self, instance): print('delete方法') instance.__dict__.pop(self.key) class People: name = Typed('name',str) #数据描述符代理属性 age = Typed('age',int) #数据描述符代理属性 def __init__(self,name,age): self.name = name #赋值操作,执行__set__方法 self.age = age #测试 p1 = People('chen',21) print(p1.__dict__) #{'name': 'chen', 'age': 21} p1 = People('chen','21') #报错:TypeError: ('传入的类型不是', <class 'int'>)
利用描述符和装饰器实现赋值类型限制(二)
#数据描述符 class Typed: def __init__(self,key,value_type): self.key = key self.value_type = value_type def __get__(self, instance, owner): print('get方法') return instance.__dict__[self.key] def __set__(self, instance, value): print('set方法') if not isinstance(value,self.value_type): #判断类型 raise TypeError('传入的类型不是',self.value_type) instance.__dict__[self.key] = value def __delete__(self, instance): print('delete方法') instance.__dict__.pop(self.key) #限制赋值类型的装饰器 def deco(**kwargs): def warapper(obj): for key,val in kwargs.items(): setattr(obj,key,Typed(key,val)) #在装饰器中给类属性实现描述符代理 return obj return warapper @deco(name=str,age=int) #@wrapper ===>People=wrapper(People) class People: #使用装饰器取代了以下两步 # name = Typed('name',str) #数据描述符代理属性 # age = Typed('age',int) #数据描述符代理属性 def __init__(self,name,age): self.name = name #赋值操作,执行__set__方法 self.age = age #测试 p1 = People('chen',21) ''' 输出: set方法 set方法 ''' print(p1.__dict__) #{'name': 'chen', 'age': 21}
描述符实现@property
class Demoproperty: def __init__(self,func): print('----+') self.func = func class People: def __init__(self,name,age): self.name = name #赋值操作,执行__set__方法 self.age = age @Demoproperty #eat=Demoproperty(eat) def eat(self): print("%s在吃饭"%self.name) p1 =People('chen',21) p1.eat.func(p1) #chen在吃饭
#非数据描述符
class Demoproperty:
def __init__(self,func):
self.func = func
def __get__(self, instance, owner):
print('get')
res = self.func(instance)
return res
class People:
def __init__(self,name,age):
self.name = name #赋值操作,执行__set__方法
self.age = age
#实现了添加描述符的操作
@Demoproperty #eat=Demoproperty(eat)
def eat(self):
print("%s在吃饭"%self.name)
p1 =People('chen',21)
p1.eat #chen在吃饭
描述符实现@calssmethod
classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等
class ClassMethod: def __init__(self,func): self.func=func def __get__(self, instance, owner): #owner是类 def feedback(*args,**kwargs): return self.func(owner,*args,**kwargs) return feedback #返回一个可以传参的方法 class People: @ClassMethod # say_hi=ClassMethod(say_hi) def say_hi(cls,name,age): print('我是%s,年龄%s' %(name,age)) People.say_hi('chen',21) #我是chen,年龄21
描述符实现@staticmethod
class StaticMethod: def __init__(self,func): self.func=func def __get__(self, instance, owner): #owner是类 def feedback(*args,**kwargs): return self.func(*args,**kwargs) return feedback #返回一个可以传参的方法 class People: @StaticMethod # say_hi=ClassMethod(say_hi) def say_hi(name,age): print('我是%s,年龄%s' %(name,age)) People.say_hi('chen',21) #我是chen,年龄21