13.16.4 描述符
1.__get__()、__set__()和__delete__()特殊方法
>>> class Celsius(object):
... def __init__(self, value=0.0):
... self.value = float(value)
... def __get__(self, instance, owner):
... return self.value
... def __set__(self, instance, value):
... self.value = float(value)
...
>>> class C(object):
... def __init__(self, value=0.0):
... print 'Current is __init__'
... self.value = float(value)
... def __get__(self, instance, owner):
... print 'Current is __get__'
... return self.value
... def __set__(self, instance, value):
... print 'Current is __set__'
... self.value = float(value)
...
>>> class D(object):
... c = C()
...
Current is __init__
>>> D().c = '10'
Current is __set__
>>> D().c
Current is __get__
10.0
>>>
描述符实际上可以是任何新式类,这种类至少实现了三个特殊方法__get__()、__set__()和__delete__()中的一个,这三个特殊方法充当描述符协议的作用。__get__()可用于得到一个属性的值,__set__()是为一个属性进行赋值的,在采用del语句(或其他,其引用计数递减)明确删除掉某个属性时会调用__delete__()方法。
还有,也不是所有的描述符都实现了__set__()方法。它们被当作方法描述符,或者更准确地说,是非数据描述符来使用。那些同时覆盖__get__()和__set__()的类被称作数据描述符。
如果你想要为一个属性写个代理,必须把它作为一个类的属性,让这个代理来为我们做所有的工作。当你用这个代理来处理对一个属性的操作时,你会得到一个描述符来代理所有的函数功能。
2. __getattribute__()特殊方法(2)
__getattribute__()因为对每个属性的实例都会调用到这个特殊的方法,这个方法被用来查找类的属性,同时也是你的一个代理,调用它可以进行属性的访问等操作。
3. 优先级别
- 类属性
- 数据描述符
- 实例属性
- 非数据描述符
- 默认为__getattr__()
函数是非绑定的。虽然静态方法是在类中被定义的,它也是非绑定的。但方法必须绑定到一个实例上,类方法必须绑定到一个类上。一个函数对象的描述符可以处理这些对象,描述符会根据函数的类型确定如何“封装”这个函数和函数被绑定的对象,然后返回调用对象。函数本身就是一个描述符,函数的__get__()方法用来处理调用对象,并将调用对象返回给你。
6. 属性和property()内建函数
属性是一种有用的特殊类型的描述符。它们是用来处理所有对实例属性的访问,其工作方式和我们前面说过的描述符相似。“一般”情况下,当你使用点属性符号来处理一个实例属性时,其实你是在修改这个实例的__dict__属性。
如果我们用property()来处理这些问题,你就可以写一个和属性有关的函数来处理实例属性的获取(getting),赋值(setting)和删除(deleting)操作。
>>> class HideX(object):
... def __init__(self, x):
... self.x = x
... @property
... def fget(self):
... print "Current is getter"
... return self.x
... @fget.setter
... def fset(self, x):
... print "Current is setter"
... assert isinstance(x, int), '"x" must be an integer!'
... self.x = x
...
>>> x = HideX(100)
>>> x.x
100
>>> x.fset = 10
Current is setter
>>> x.fget
Current is getter
10
>>>
>>> class C(object):
... def __init__(self):
... self._x = None
... def getx(self): print "Getting x"; return self._x
... def setx(self, value): print "setting x"; self._x = value
... def delx(self): print "deleting x"; del self._x
... x = property(getx, setx, delx, "I'm the 'x' property.")
...
>>> c = C()
>>> c.x = 1000
setting x
>>> c.x
Getting x
1000
>>> del c.x
deleting x
>>>