描述符就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议
__get__():调用一个属性时触发
__set__():为一个属性赋值时触发 __delete__():使用delete删除属性时触发
描述符的参数
class Str(): def __get__(self, instance, owner): print('from __get__...') print('self',self) #<__main__.Str object at 0x02D80210>类Str的对象即p1的name属性 print('instance',instance)#<__main__.People object at 0x02D801D0>类People的对象即p1 print('owner',owner) #<class '__main__.People'>类People def __set__(self, instance, value): print('from __set__...') print('self',self) #<__main__.Str object at 0x02D80210> print('instance',instance)#<__main__.People object at 0x02D801D0> print('value',value) # lary p1.name属性的value def __delete__(self, instance): print('from __delete__...') print('self',self) #<__main__.Str object at 0x02D80210> print('instance',instance)#<__main__.People object at 0x02D801D0> class People(): name = Str() def __init__(self,name,age): self.name = name self.age = age p1 = People('lary',18) p1.name del p1.name
触发
描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)
#描述符类产生的实例进行属性操作并不会触发三个方法的执行 class Foo(): def __get__(self, instance, owner): print('use get') def __set__(self, instance, value): print('use set') def __delete__(self, instance): print('use delete') f1 = Foo() f1.name='lary'
#描述符代理另外一个类的类属性时才会触发 class Str: def __get__(self, instance, owner): print('Str调用') def __set__(self, instance, value): print('Str设置...') def __delete__(self, instance): print('Str删除...') class People: name=Str() def __init__(self,name,age): #name被Str类代理 self.name=name self.age=age p1 = People('lary',18) p1.name
分类
描述符分为数据描述符和非数据描述符
#类别一 数据描述符:至少实现了__get__()和__set__() class Str: def __get__(self, instance, owner): print('Str调用') def __set__(self, instance, value): print('Str设置...') #类别二 非数据描述符:没有实现__set__() class Str: def __get__(self, instance, owner): print('Str调用')
优先级
描述符本身应该被定义成新式类,被代理的类也应该是新式类
必须把描述符定义成这个类的类属性,不能定义到构造函数中
描述符与类的属性有优先级顺序,必须遵循该优先级
描述符与类的优先级由高到低 类属性 数据描述符 实例属性 非数据描述符 找不到的属性触发__getattr__()
class Str: def __get__(self, instance, owner): print('Str调用') def __set__(self, instance, value): print('Str设置...') def __delete__(self, instance): print('Str删除...') class People: name=Str() def __init__(self,name,age): #name被Str类代理,age被Int类代理, self.name=name self.age=age print(People.name) People.name = 'lary' print('before',People.__dict__) print('---') del People.name print('after',People.__dict__)
class Str: def __get__(self, instance, owner): print('Str调用') def __set__(self, instance, value): print('Str设置...') def __delete__(self, instance): print('Str删除...') class Int: def __get__(self, instance, owner): print('Int调用') def __set__(self, instance, value): print('Int设置...') def __delete__(self, instance): print('Int删除...') class People: name=Str() age=Int() def __init__(self,name,age): #name被Str类代理,age被Int类代理, self.name=name self.age=age p1=People('lily',18) p1.name p1.age
class Foo: def func(self): print('我胡汉三又回来了') f1 = Foo() f1.func() #调用类的方法,也可以说是调用非数据描述符,函数是一个非数据描述符对象 print(dir(Foo.func)) print(hasattr(Foo.func,'__delete__')) print(hasattr(Foo.func,'__set__')) print(hasattr(Foo.func,'__get__')) f1.func = '这是实例属性啊' print(f1.func) del f1.func class Foo: #至少实现了__set__和__get__方法的为数据描述符 def __set__(self, instance, value): print('set') def __get__(self, instance, owner): print('get') class Room: name=Foo() def __init__(self,name,width,length): self.name=name self.width=width self.length=length r1 = Room('厕所',1,1) r1.name r1.name='厨房' class Foo: def __get__(self, instance, owner): #只实现了__get__方法的为非数据描述符 print('get') class Room: name=Foo() def __init__(self,name,width,length): self.name=name self.width=width self.length=length r1 = Room('厕所',1,1) print(r1.name) r1.name = '厨房' print(r1.name)
class Foo(object): # def __getattribute__(self, item): # print('能不能找到都会来找我', item) def func(self): print('我胡汉三又回来了') f1=Foo() # f1.xxxxx # f1.func print(f1.__dict__) f1.func() class animal(object): def __getattribute__(self, item): return object.__getattribute__(self,item)() def eat(self): print('eating...') #print(animal.__dict__) cat = animal() #print(cat.__dict__) cat.eat #当获取属性时,直接return object.__getattribute__(self,*args,**kwargs) #如果需要获取某个方法的返回值时,则需要在函数后面加上一个()即可,如果不加的话,返回的是函数引用地址 #在__getattribute__方法里面,不能用self.xxx这种方式调用。因为调用类的属性每次都会强制调用__getattribute__,所以会导致递归调用
使用
class Typed: def __init__(self,name,expected_type): self.name=name self.expected_type=expected_type def __get__(self, instance, owner): print('get--->',instance,owner) if instance is None: return self return instance.__dict__[self.name] def __set__(self, instance, value): print('set--->',instance,value) if not isinstance(value,self.expected_type): raise TypeError('Expected %s' %str(self.expected_type)) instance.__dict__[self.name]=value def __delete__(self, instance): print('delete--->',instance) instance.__dict__.pop(self.name) class People: name=Typed('name',str) age=Typed('name',int) salary=Typed('name',float) def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1=People(123,18,3333.3) p1=People('egon','18',3333.3) p1=People('egon',18,3333) p1=People('egon',18,3333.3)
如果我们的类有很多属性,可以使用类的装饰器来使用描述符
def decorate(cls): print('类的装饰器开始运行啦') return cls @decorate class People: def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1 = People('lary',18,4321.3) print(p1.name)
def typeassert(**kwargs): def decorate(cls): print('类的装饰器开始运行啦',kwargs) return cls return decorate @typeassert(name=str,age=int,salary=float) class People: def __init__(self,name,age,salary): self.name=name self.age=age self.salary=salary p1 = People('lary',18,3434.1) print(p1.name)
#描述符与装饰器的应用 class Typed: def __init__(self,name,expected_type): self.name=name self.expected_type=expected_type def __get__(self, instance, owner): print('get--->',instance,owner) if instance is None: return self return instance.__dict__[self.name] def __set__(self, instance, value): print('set--->',instance,value) if not isinstance(value,self.expected_type): raise TypeError('Expected %s' %str(self.expected_type)) instance.__dict__[self.name]=value def __delete__(self, instance): print('delete--->',instance) instance.__dict__.pop(self.name) def typeassert(**kwargs): def decorate(cls): print('类的装饰器开始运行啦',kwargs) for name,expected_type in kwargs.items(): setattr(cls,name,Typed(name,expected_type)) return cls return decorate @typeassert(name=str,age=int,salary=float) class People: def __init__(self,name,age,salary): self.name = name self.age = age self.salary = salary print(People.__dict__) p1 = People('lary',18,2343.2)
自定义属性
# property class Animal(): def __init__(self,name): self.name = name @property def eat(self): return self.name animal1 = Animal('cat') print(animal1.eat) #自定义property #调用传过来的func函数,将对象(instance)作为参数(即类中函数需要的参数self) class Lazyproperty(): def __init__(self,func): self.func = func def __get__(self, instance, owner): print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()') if instance is None: return self return self.func(instance) class Room(): def __init__(self,name,width,length): self.name = name self.width = width self.length = length @Lazyproperty #area = Lazyproperty(area) def area(self): return self.width * self.length r1 = Room('lary',1,1) print(r1.area)
class Lazyproperty(): def __init__(self,func): self.func = func def __get__(self, instance, owner): print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()') if instance is None: return self else: print('--->') value = self.func(instance) setattr(instance,self.func.__name__,value) #计算一次就缓存到实例的属性字典中 print('第一次访问__get__方法') return value class Room(): def __init__(self,name,width,length): self.name = name self.width = width self.length = length @Lazyproperty #area = Lazyproperty(area) def area(self): return self.width * self.length r1 = Room('lary',1,2) print(r1.area) #先从自己的属性字典找,没有再去类的中找,然后触发了area的__get__方法 print(r1.area) #先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算 class Lazyproperty(): def __init__(self,func): self.func = func def __get__(self, instance, owner): print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()') if instance is None: return self else: print('第n次访问get方法') value = self.func(instance) #setattr(instance,self.func.__name__,value) #计算一次就缓存到实例的属性字典中 instance.__dict__[self.func.__name__]=value return value def __set__(self, instance, value): print('hhhhh') class Room(): def __init__(self,name,width,length): self.name = name self.width = width self.length = length @Lazyproperty #area = Lazyproperty(area) def area(self): return self.width * self.length r1 = Room('lary',1,2) print(r1.area) print(r1.area) print(r1.area)#缓存功能失效每次都去找描述符了.因为描述符实现了set方法,它由非数据描述符变成了数据描述符,数据描述符比实例属性有更高的优先级,因而所有的属性操作都去找描述符了
#自定义@classmethod class Animal(): feature = 'live' def __init__(self,name): self.name = name @classmethod def feature_animal(cls): return cls.feature a1 = Animal.feature_animal() print(a1) class lazyClassMethod(): def __init__(self,func): self.func = func def __get__(self, instance, owner): def feedback(): print('在这里可以加功能哦') return self.func(owner) return feedback class Animal(): feature = 'live' def __init__(self,name): self.name = name @lazyClassMethod def feature_animal(cls): return cls.feature a1 = Animal('cat') res=a1.feature_animal() print(res) #带参数的自定义@classmethod class lazyClassMethod(): def __init__(self,func): self.func = func def __get__(self, instance, owner): def feedback(*args,**kwargs): print('在这里可以加功能哦') return self.func(owner,*args,**kwargs) return feedback class Animal(): feature = 'live' def __init__(self,name): self.name = name @lazyClassMethod def feature_animal(cls,msg): print('animal is %s:%s'%(cls.feature,msg)) a1 = Animal('cat') res=a1.feature_animal('so cute')
#自定义staticmethod class Animal(): @staticmethod def eat(food,water): print('animal is eating %s drink %s'%(food,water)) a1 = Animal() a1.eat('meat','water') class lazyStaticmethod(): def __init__(self,func): self.func = func def __get__(self, instance, owner): def feedback(*args,**kwargs): return self.func(*args,**kwargs) return feedback class Animal(): @lazyStaticmethod #eat = lazyStaticmethod(eat) def eat(food,water): print('animal is eating %s drink %s'%(food,water)) a1 = Animal() a1.eat('food','water')