#描述符是很多高级库,和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件 #描述符分为两种: #第一种是数据描述符:至少实现了 __get__()和__set__() #第二种为非数据描述符:没有实现__set__() #http://www.cnblogs.com/linhaifeng/articles/6204014.html#_label6 #描述符是什么:描述符本质是一个新式类, # 这个新式类至少实现了__getter__()__set__()__delete__()中的一个,这也被称为描述符 #描述符是干什么的:描述符的作用是用来代理另外一个类的类属性的 # (必须把描述符定义成这个类的类属性,不能定义到构造函数中) #__get__():调用一个属性时,触发 #__set__():为一个属性赋值时,触发 #__delete__():采用del删除属性时,触发 #直接用描述符本身的对象是没有调用的效果的 class Foo: def __get__(self, instance, owner):#取值触发 print("get") def __set__(self, instance, value):#赋值触发 print("set",instance,value) # instance.__dict__["x"]=value #如果进行这步操作 就会在b1中的字典传入值 def __delete__(self, instance):#删除触发 print("delete") f1=Foo() f1.name='pl' #调用时候没有触发set 函数 自己用时候不可能执行 必须有另外一个类 print(f1.name) class Bar: x=Foo() #x被Foo描述 也就是被Foo 所代理了 #在何时 # 定义的是一个类属性 这个属性是Foo 实例化的结果 def __init__(self,n): self.x=n #就类似b1.x=10 #因为有了x=Foo这个属性调用了描述符 所以 触发 如果没有Foo 便会打印自己的 b1=Bar(10)#加入没有Foo找的是自己的属性字典 print(b1.__dict__) #如果没有被代理的属性 就是自己的 b1.y=1111111111111 print(b1.__dict__) #自己的字典当中在没有调用描述符时候 便会在自己的函数中执行 #在何地 # f2=Bar() # f2.x #调用Foo的实例 一个描述符的对象 # del f2.x #调用了描述符里的内容 # # print(f2.x) #4 注意事项: # 一 描述符本身应该定义成新式类,被代理的类也应该是新式类 # 二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中 # 三 要严格遵循该优先级,优先级由高到底分别是 # 1.类属性 # 2.数据描述符 # 3.实例属性 # 4.非数据描述符 # 5.找不到的属性触发__getattr__()
描述符的优先级:
class Foo: def __get__(self, instance, owner):#取值触发 print("get") def __set__(self, instance, value):#赋值触发 print("set",instance,value) # instance.__dict__["x"]=value #如果进行这步操作 就会在下一个类中调用字典传入值 def __delete__(self, instance):#删除触发 print("delete") class Bar: x=Foo() #这就显示描述符在何地 b1=Bar() b1.x=1 #实例化中 会现在自己的属性字典当中寻找 找不到 则在类中找 # 类对应的是对象,对象里面是一个描述符 print(Bar.x)#触发了get属性 # 没有触发 类属性 会高于 描述符的属性 所以只在自己的字典中触发 del b1.x # 主要先对自己的类实例化 然后去 找描述符 # 优先级的大小如下: # 类属性>数据描述符 # 数据描述符>实例属性 # 实例属性 >非数据描述符 # 非数据描述符 >找不到 print("----->"*20) b2=Bar() Bar.x=1111111 #当类属性重新对x赋值 便不会触发描述符的操作 b2.x #这就证明了类属性的优先级高于数据描述符 高于 实例属性
描述符的应用:
#__get__():调用一个属性时,触发 #__set__():为一个属性赋值时,触发 #__delete__():采用del删除属性时,触发 #直接用描述符本身的对象是没有调用的效果的 class Typed: def __init__(self,key,expect_type): self.key=key self.expect_type=expect_type def __get__(self, instance, owner):#owner 是p1的类 print('get方法') # print('instance参数【%s】'%instance) # print('owner参数【%s】'%owner) return instance.__dict__[self.key] #get方法 会找return 里面的key def __set__(self, instance, value):#set里接收的instance 就是p1本身 print('set方法') if not isinstance(value,self.expect_type): # print("你传入的类型错误") # return #相当于终止函数 raise TypeError('%s传入的类型错误,不是%s'%(self.key,self.expect_type)) #直接结束 # print('instance参数【%s】' % instance) # print('value参数【%s】'%value ) instance.__dict__[self.key]=value def __delete__(self, instance): print("delete方法") instance.__dict__.pop(self.key) # print('instance参数【%s】' % instance) #p1的实例 class People: name=Typed('name',str) #name属性被taped代理了 触发了 t1.__set__ age=Typed('age',int) def __init__(self,name,age,salary):#没有对这些属性加上类型限制 self.name=name self.age=age self.salary=salary p1=People('pl',18,2000000)#实列化的过程 #触发了get 方法 # 实列属性>非数据描述符 所以不会触发get 函数 print(p1.salary) print(p1.__dict__) # # #加上类型限制 # print("下一步") # p1.name # 会触发了 get 方法 # # print(p1.name) # p1.name='egon'#为一个属性赋值时候触发 修改了一下 还是触发了set # #做了一次属性的更改 # print(p1.__dict__) # del p1.name #触发了删除的操作 # #数据描述符的优先级高于属性 所以属性的操作都是 去找描述符去了 # print(p1.__dict__) p1=People(111,18,2000000) print(p1.__dict__)