第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里面。

原文地址:https://www.cnblogs.com/luoxun/p/13642268.html