这里引用一段代码来试图说明我对描述符的理解
class A(): #定义关于摄氏度的描述符
def __init__(self,value = 30.0):
self.value = float(value)
def __get__(self,instance,owner):
return self.value
def __set__(self,instance,value):
self.value = float(value)
class B(): #定义关于华氏度的描述符
def __get__(self,instance,owner): #摄氏度转华氏度
return instance.cel*1.8+32
def __set__(self,instance,value): #华氏度转摄氏度
instance.cel = (float(value)-32)/1.8
class T():
cel = A()
har = B() #定义摄氏度属性cel与华氏度属性har
#运行结果如下
In[]: a = T()
In[]: a.cel
Out[]: 30.0
In[]: a.har
Out[]: 86.0
In[]: a.har = 70
In[]: a.cel
Out[]: 21.11111111111111
首先明确描述符的定义:
描述符就是一个具有绑定行为的对象属性,其属性访问将由描述符协议中的方法覆盖。这些方法为__get__、set__和__delete。如果这些方法中的任何一个针对某个对象定义,那么它就被认为是一个描述符
__get__方法是指当前属性的值被取得时则调用该方法
__set__方法是指当前属性的值被修改时则调用该方法
__delete__方法是指当前属性的值被删除时则调用该方法
参数instance表示当前实例化对象
参数owner表示当前实例化对象所属的类
来看代码
当使用a来实例化T类时,此时a内已经含有cel属性
a=T()
a.cel = 30
因为在a实例化T类时,首先自动调用了 A() 类内的__init__()方法,此时已经传值给value,使得a.cel得到value的值为30。
命令行调用a.har属性,返回得到86.0
In[] : a.har
Out[] : 86.0
def __get__(self,instance,owner): #B类中__get__方法 属性值被取得时调用
return instance.cel*1.8+32
这里是因为使用a.har时,自动调用了B类中的__get__()方法,则取得a.har的值
此处函数中的instance参数就是指当前实例a,返回值即是a.cel参与运算的运算结果,即是调用a.har的结果
In[]:a.har = 70
In[]:a.cel
Out[]:21.11111111111
此处修改属性a.har的值,再查看a.cel属性的值,发现随之改变
def __set__(self,instance,value): #B类中的__set__方法 属性值被修改时调用
instance.cel = (float(value)-32)/1.8
def __get__(self,instance,owner): #A类中的__get__方法 属性值被取得时调用
return self.value
a.har = 70这句语句触发时,标识实例化对象a的属性har值被修改,则此时自动调用har依托的B类中的__set__方法,此时传入函数的参数instance=实例化对象a,value = 70,进行将(70-32)/1.8 的结果赋值给a.cel的操作。
而给a.cel赋值,即调用了cel依托的A类中__set__方法并传入参数value = (70-32)/1.8 ,使得函数内self.value = (70-32)/1.8。
再次查看a.cel的值,则调用cel依托A类中的__get__方法返回当前cel属性的值,即为上赋值给该属性的(70-32)/1.8。
解释代码过程我使用了‘依托’这个词来表示属性与描述符之间的关系,实际上并不能这么理解,但是能力有限,也只能这样描述了。
整个代码过程相当于,使用a来对T类进行实例化时,在T类内含有属性cel与属性har,则实例化对象a含有cel与har两属性。通过cel = A()语句来实现cel与A类描述符的相关联,属性har同理。此时对两属性进行重写函数的操作时,则会自动调用你重写的相关操作的方法
第一次写这种博客,描述能力实在有限,大家愿意看下去的我在这里多谢了。如果理解有什么问题也欢迎大家评论区提出来或者留言,感谢感谢