Python3基础-描述符
描述符的概念
描述符就是一个“绑定行为“的对象属性,在描述符协议中,它可以通过方法充写属性的访问。这些方法有get(),set(),delete().如果这些方法中任何一个被定义在一个对象中,这个对象就是一个描述符
属性:dict(每个对象均具备该属性) ==字典类型,存放本对象的属性,key(键)即为属性名,value(值)即为属性的值,形式为{attr_key : attr_value}。
class People(): country = 'China' def __init__(self,name,age): self.name = name self.age =age p1 = People('susu',28) print(People.__dict__) print('='*100) print(p1.__dict__)
以上代码执行结果如下:
{'__module__': '__main__', 'country': 'China', '__init__': <function People.__init__ at 0x032E9738>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None} ==================================================================================================== {'name': 'susu', 'age': 28}
实例p1 的属性并不包含country ,country 是属于类People
魔法方法:__get__(), __set__(), __delete__()
方法的原型为:
1. __get__(self, instance, owner)
2. __set__(self, instance, value)
3. __delete__(self, instance)
self : 描述符实例
instance:相当于例子中的实例book
value: 就是要赋予的值
==简单的代码==
class Desc(): def __get__(self, instance, owner): print("__get__") print("self=",self) print("instance=",instance) print("owner=",owner) print("=="*50,"\n") class People(): x = Desc() p1 = People() p1.x print(People.__dict__) print('-'*100) print(p1.__dict__) """ 执行结果如下 __get__ self= <__main__.Desc object at 0x02C40130> instance= <__main__.People object at 0x02C400F0> owner= <class '__main__.People'> ==================================================================================================== {'__module__': '__main__', 'x': <__main__.Desc object at 0x02C40130>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None} ---------------------------------------------------------------------------------------------------- {} """
可以从上面的代码看到,实例化People后,调用对象p1访问其属性x,会自动调用类的Desc的__get__方法
1、self= <__main__.Desc object at 0x02C40130> 是Desc的实例对象,也是People的属性x 2、instance= <__main__.People object at 0x02C400F0> 是People的实例对象,则是实例化后变量p1 3、owner= <class '__main__.People'> 是Peoplede 这个类;因为Desc是包含在它的内部,所以People拥有所属权
描述符的对象 x 其实是类People的类属性,那么可不可以把它变成实例属性呢
class Desc(): def __init__(self,key): self.key = key def __get__(self, instance, owner): print("__get__") print("self.key==",self.key) def __set__(self, instance, value): print("__set__") def __delete__(self, instance): print("__delete__") instance.__dict__.pop(self.key) #删除 class People(): x = Desc('x') def __init__(self): self.age =Desc('age') p1 = People() print(p1.x) print(p1.age) print(p1.__dict__) print(People.__dict__)
以上代码执行结果如下:
__get__ self.key== x None <__main__.Desc object at 0x012F09B0> {'age': <__main__.Desc object at 0x012F09B0>} {'__module__': '__main__', 'x': <__main__.Desc object at 0x012F00F0>, '__init__': <function People.__init__ at 0x013197C8>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
如果是实例属性,则没有调用__get__方法。因为调用p1.age时,首先会去调用People的 __getattribute__() 方法。而age是一个描述符类,所以解释器将该方法转化为TestDesc.__dict__[‘age’].__get__(p1, People), 但是People 并没有 age这个属性,age 是属于实例对象的,所以,只能忽略了。
如果 类属性的描述符对象 和 实例属性描述符的对象 同名时
class Desc(): def __init__(self,key): self.key = key def __get__(self, instance, owner): print("__get__") print("get里面的self",self) print("get里面的instance",instance) print("get里面的owner",owner) print("self.key==",self.key) print("=="*50,"\n") return self.key def __set__(self, instance, value): print("__set__") print("set里面的self",self) print("set里面的instance",instance) print("set里面的owner",value) # instance.__dict__[self.key]=value class People(): name = Desc('name') #age = Desc('age') def __init__(self,name,age): self.name = name #self.age = age p1 = People('susu',200) print(p1.name) print(p1.__dict__) """ 执行结果如下: __set__ set里面的self <__main__.Desc object at 0x031909D0> set里面的instance <__main__.People object at 0x031909F0> set里面的owner susu __get__ get里面的self <__main__.Desc object at 0x031909D0> get里面的instance <__main__.People object at 0x031909F0> get里面的owner <class '__main__.People'> self.key== name ==================================================================================================== name {} """
每次属性查找,这个协议的方法实际上是由对象的特殊方法getattribute()调用。每次通过点号(ins.attribute)或者getattr(ins, ‘attribute’)
函数调用都会隐式的调用getattribute(),它的属性查找的顺序如下: 1. 验证属性是否为实例的类对象的数据描述符 2. 查看该属性是否能在实例对象的dict中找到 3. 查看该属性是否为实例的类对象的非数据描述符
class Desc(): def __init__(self,name): self.name = name def __get__(self, instance, owner): return self.name def __set__(self, instance, value): print('instance==', instance) print('value==', value) class People(): _x = Desc('name') def __init__(self,x): self._x = x p1 = People(10) print(p1.__dict__) #p1输出为空 {}
class Desc():def __get__(self, instance, owner): print("get")def __set__(self, instance, value): # print('self==',self) print('instance==', instance) print('value==', value) instance.__dict__['_x'] = value #固定写死的 class People(): _x = Desc('_x') def __init__(self,x): self._x = x p1 = People(10) print(p1.__dict__) # {'_x': 10}
只要是内部定义了方法get,set,delete中的一个或多个,就可以称为描述符。如果实现了set和get,则称为数据描述符;如果只实现了get,则称为非数据描述符。
对象属性的访问顺序: 类属性>数据描述符>实例属性>非数据描述符>找不到的属性触发__getattr__()
class Desc(): def __init__(self,name): self.name = name def __get__(self, instance, owner): print("__get__") # print("name==",self.name) # print("=="*50,"\n") return self.name def __set__(self, instance, value): # print('self==',self) print('instance==', instance) print('value==', value) instance.__dict__['_x'] = value class People(): x = Desc('susu') #x 是一个数据描述符 p1 = People() p1.x #触发了 __get__方法 People.x = 10 #重新设置类属性 p1.x #就没有调用了 __get__方法 print(p1.x) #覆盖了数据描述符
class Desc(): def __init__(self,name): self.name = name def __get__(self, instance, owner): print("__get__") # print("name==",self.name) # print("=="*50,"\n") return self.name def __set__(self, instance, value): # print('self==',self) print('instance==', instance) print('value==', value) instance.__dict__['_x'] = value class People(): x = Desc('susu') #x 是一个数据描述符 p1 = People() p1.x =1 print(p1.x) #调用__get__方法
class Desc(): def __init__(self,name): self.name = name def __get__(self, instance, owner): print("__get__") # print("name==",self.name) # print("=="*50,"\n") return self.name class People(): x = Desc('susu') #x 是一个数据描述符 p1 = People() p1.x =1 print(p1.x) #返回的是 1
class Desc(): def __init__(self,name): self.name = name def __get__(self, instance, owner): print("__get__") # print("name==",self.name) # print("=="*50,"\n") return self.name class People(): x = Desc('susu') #x 是一个数据描述符 def __getattr__(self, item): print('找不到') p1 = People() p1.x # 会调用get方法 p1.y # 找不到才调用__getattr__方法 """ 执行结果: __get__ 找不到 """
========================描述符应用========================
class Desc(): def __init__(self,key): self.key = key def __get__(self, instance, owner): print("__get__")return instance.__dict__[self.key] #获取 def __set__(self, instance, value): print("__set__") instance.__dict__[self.key]=value #写入 def __delete__(self, instance): print("__delete__") instance.__dict__.pop(self.key) #删除 class People(): name = Desc('name') age = Desc('age') def __init__(self,name,age): self.name = name self.age = age p1 = People('susu',200) print(p1.name) print(p1.age) print(p1.__dict__) del p1.age print(p1.__dict__)
以上代码执行结果如下:
__set__ __set__ __get__ susu __get__ 200 {'name': 'susu', 'age': 200} __delete__ {'name': 'susu'}
为什么要用描述符
#name 输入的类型是str类型,如果不是str类型就报错 #age 输入的类型是int类型,如果不是int类型就报错 class Desc(): def __init__(self,key,expected_type): self.key = key self.expected_type = expected_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.expected_type): raise TypeError("%s 类型必须输入%s类型"%(self.key,self.expected_type)) instance.__dict__[self.key]=value #写入 def __delete__(self, instance): print("__delete__") instance.__dict__.pop(self.key) class People(): name = Desc('name',str) age = Desc('age',int) def __init__(self,name,age,salary): self.name = name self.age = age self.salary = salary p1 = People('susu',28,75222) print(p1.name) print(p1.age) print(p1.__dict__) print(People.__dict__)