课时46:魔法方法:描述符(property的原理)

 目录:

  一、描述符(property的原理)

  二、课时46课后习题及答案

 

**********************************

一、描述符(property的原理)

**********************************

本节要讲的内容叫作描述符,用一句话解释,描述符就是将某种特殊类型的类的实例指派给另一个类的属性。那什么是特殊类型的类呢?就是至少要在这个类里边定义__get__()、__set__()或__delete__()三个特殊方法中的任意一个。

下表列举了描述符相关的魔法方法:

__get__(self, instance, owner)     用于访问属性,它返回属性的值
__set__(self, instance, value)     将在属性分配操作中调用,不返回任何内容
__delete__(self, instance)         控制删除操作,不返回任何内容

 举个直观的例子:

class MyDescriptor:
    def __get__(self, instance, owner):
        print("getting...", self, instance, owner)
        
    def __set__(self, instance, value):
        print("setting...", self, instance, value)
        
    def __delete__(self, instance):
        print("deleting...", self, instance)

class Test:
    x = MyDescriptor()

 由于MyDescriptor实现了__get__()、__set__()和__delete__()方法,并且将它的类实例指派给Test类的属性,所以MyDescriptor就是所谓描述符类。到这里,大家有没有看到property()的影子?

好,实例化Test类,然后尝试着对x属性进行各种操作,看看描述符类会有怎样的响应:

>>> test = Test()
>>> test.x
getting... <__main__.MyDescriptor object at 0x000001559C681CF8> <__main__.Test object at 0x000001559C67A588> <class '__main__.Test'>

当访问x属性的时候,Python会自动调用描述符的__get__()方法,几个参数的内容分别是:self是描述符类自身的实例;instance是这个描述符的拥有者所在的类的实例,在这里也就是Test类的实例;owner是这个描述符的拥有者所在的类本身。

>>> test.x = 'X-man'
setting... <__main__.MyDescriptor object at 0x000001559C681CF8> <__main__.Test object at 0x000001559C67A588> X-man

对x属性进行赋值操作的时候,Python会自动调用__set__()方法,前两个参数跟__get__()方法是一样的,最后一个参数value是等号右边的值。

最后一个del操作也是同样的道理:

>>> del test.x
deleting... <__main__.MyDescriptor object at 0x000001559C681CF8> <__main__.Test object at 0x000001559C67A588>

只要弄清楚描述符,那么property的秘密就不再是秘密了!property事实上就是一个描述符类。下边就定义一个属于我们自己的MyProperty:

class MyProperty:
    def __init__(self, fget=None, fset=None, fdel=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        
    def __get__(self, instance, owner):
        return self.fget(instance)
    
    def __set__(self, instance, value):
        self.fset(instance, value)
        
    def __delete__(self, instance):
        self.fdel(instance)

class C:
    def __init__(self):
        self._x = None
        
    def getX(self):
        return self._x
    
    def setX(self, value):
        self._x = value
        
    def delX(self):
        del self._x
        
    x = MyProperty(getX, setX, delX)
>>> c = C()
>>> c.x = 'X-man'
>>> c.x
'X-man'
>>> c._x
'X-man'
>>> del c.x
>>> c.x
Traceback (most recent call last):
  File "<pyshell#5>", line 1, in <module>
    c.x
  File "C:\Users\14158\Desktop\MyProperty.py", line 8, in __get__
    return self.fget(instance)
  File "C:\Users\14158\Desktop\MyProperty.py", line 21, in getX
    return self._x
AttributeError: 'C' object has no attribute '_x'

看,这不就实现了property()函数了嘛,简单吧?!

最后讲一个实例:

先定义一个温度类,然后定义两个描述符类用于描述摄氏度和华氏度两个属性。
要求两个属性会自动进行转换,也就是说你可以给摄氏度这个属性赋值,然后打印的华氏度属性是自动转换后的结果。
class Celsius:
    def __init__(self, value = 26.0):
        self.value = float(value)

    def __get__(self, instance, owner):
        return self.value

    def __set__(self, instance, value):
        self.value = float(value)

class Fahrenheit:
    def __get__(self, instance, owner):
        return instance.cel * 1.8 + 32

    def __set__(self, instance, value):
        instance.cel = (float(value) - 32) / 1.8

class Temperature:
    cel = Celsius()
    fah = Fahrenheit()
>>> temp = Temperature()
>>> temp.cel
26.0
>>> temp.fah
78.80000000000001

 

*******************************

二、课时46课后习题及答案

*******************************

 

posted @ 2018-08-25 20:16  那是个好男孩  阅读(258)  评论(0编辑  收藏