第46讲:魔方方法——描述符(property的原理)
一 描述符相关知识
1 定义:描述符就是将某种特殊类型的类的实例指派给另一个类的属性
- 特殊类型的类要满足的条件是:至少要实现以下三个方法其中一个
- __get__(self,instance,owner):用于访问属性,它返回属性的值
- __set__(self,instance,value):将在属性分配(赋值)操作中调用,不返回任何内容
- __delete__(self,instance):控制删除操作,不返回任何内容
2 举例:property函数的实现
1 >>> class MyProperty(object): 2 ... def __init__(self,fget=None,fset=None,fdel=None): 3 ... self.fget = fget 4 ... self.fset = fset 5 ... self.fdel = fdel 6 ... def __get__(self,instance,owner): 7 ... return self.fget(instance) 8 ... def __set__(self,instance,value): 9 ... self.fset(instance,value) 10 ... def __del__(self,instance): # 注意,这里应该用delete,不是缩写,否则后面会报错 11 ... self.fdel(instance) 12 ... 13 >>> class C(object): 14 ... def __init__(self): 15 ... self._x = None 16 ... def getX(self): 17 ... return self._x 18 ... def setX(self,value): 19 ... self._x = value 20 ... def delX(self): 21 ... del self._x 22 ... x = MyProperty(getX,setX,delX) 23 ... 24 >>> c = C() 25 >>> c.x = "abc" 26 >>> c.x 27 'abc' 28 >>> c._x 29 'abc' 30 >>> del c._x 31 >>> del c.x 32 Traceback (most recent call last): 33 File "<stdin>", line 1, in <module> 34 AttributeError: __delete__
3 练习
要求:
- 先定义一个温度类,然后定义两个描述符类,用于描述摄氏度和华氏度两个属性
- 要求两个属性会自动进行转换,也就是说你可以给摄氏度这个属性赋值,然后打印的华氏度属性是自动转换后的结果
代码:
1 class Celsius(object): 2 def __init__(self,value = 26.0): 3 self.value = float(value) 4 def __get__(self,instance,owner): 5 return self.value 6 def __set__(self,instance,value): 7 self.value = float(value) 8 9 class Fahrenheit(object): 10 def __get__(self,instance,owner): 11 return instance.cel * 1.8 + 32 # 完成摄氏度到华氏度的转换 12 def __set__(self,instance,value): 13 instance.cel = (float(value) - 32) / 1.8 # 完成华氏度到摄氏度的转换 14 15 # 描述符类 16 class Temperature(object): 17 cel = Celsius() # cel是类Temperature的属性,但同时它也是Celsius类的实例 18 fah = Fahrenheit()
执行结果:
1 >>> import class46_Temp as T 2 >>> temp = T.Temperature() 3 >>> temp.cel 4 26.0 5 >>> temp.fah 6 78.80000000000001 7 >>> temp.cel = 30 8 >>> temp.fah 9 86.0 10 >>> temp.fah = 100 11 >>> temp.cel 12 37.77777777777778
执行过程:
temp.fah ——访问Temperature类的fah属性,该属性是Fahrenheit类的实例,此时会调用Fahrenheit类的__get__方法,从而计算出默认的摄氏度26对应的华氏度的值:78.80000000000001
temp.cel = 30 ——对Temperature类的cel属性进行赋值,该属性是Celsius类的实例,此时会调用Celsius类的__set__方法,将self.value的值赋值为30,然后再通过temp.fah访问fah属性,得到30对应的华氏度的值:86.0
temp.fah = 100 ——对Temperature类的fah属性进行赋值,该属性是Fahrenheit类的实例,此时会调用Fahrenheit类的__set__方法,计算华氏度100对应的摄氏度的值:37.77777777777778,并将计算结果赋值给instance.cel也就是temp.cel;因为此时进行了赋值操作,相当于调用了Celsius类的__set__方法,将计算后的结果37.77777777777778转化为浮点数存储在self.value里面。