所有基础文章修改完毕
This commit is contained in:
143
Article/PythonBasis/python10/4.md
Normal file
143
Article/PythonBasis/python10/4.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# 四、对象的描述器 #
|
||||
|
||||
一般来说,一个描述器是一个有“绑定行为”的对象属性 (object attribute),它的访问控制被描述器协议方法重写。
|
||||
|
||||
这些方法是 `__get__()`, `__set__()` , 和 `__delete__()` 。
|
||||
|
||||
有这些方法的对象叫做描述器。
|
||||
|
||||
默认对属性的访问控制是从对象的字典里面 (`__dict__`) 中获取 (get) , 设置 (set) 和删除 (delete) 。
|
||||
|
||||
举例来说, `a.x` 的查找顺序是, `a.__dict__['x']` , 然后 `type(a).__dict__['x']` , 然后找 `type(a)` 的父类 ( 不包括元类 (metaclass) ).如果查找到的值是一个描述器, Python 就会调用描述器的方法来重写默认的控制行为。
|
||||
|
||||
这个重写发生在这个查找环节的哪里取决于定义了哪个描述器方法。
|
||||
|
||||
注意, 只有在新式类中时描述器才会起作用。在之前的篇节中已经提到新式类和旧式类的,有兴趣可以查看之前的篇节来看看,至于新式类最大的特点就是所有类都继承自 type 或者 object 的类。
|
||||
|
||||
在面向对象编程时,如果一个类的属性有相互依赖的关系时,使用描述器来编写代码可以很巧妙的组织逻辑。在 Django 的 ORM 中,models.Model 中的 InterField 等字段, 就是通过描述器来实现功能的。
|
||||
|
||||
我们先看下下面的例子:
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
class User(object):
|
||||
def __init__(self, name='两点水', sex='男'):
|
||||
self.sex = sex
|
||||
self.name = name
|
||||
|
||||
def __get__(self, obj, objtype):
|
||||
print('获取 name 值')
|
||||
return self.name
|
||||
|
||||
def __set__(self, obj, val):
|
||||
print('设置 name 值')
|
||||
self.name = val
|
||||
|
||||
|
||||
class MyClass(object):
|
||||
x = User('两点水', '男')
|
||||
y = 5
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
m = MyClass()
|
||||
print(m.x)
|
||||
|
||||
print('\n')
|
||||
|
||||
m.x = '三点水'
|
||||
print(m.x)
|
||||
|
||||
print('\n')
|
||||
|
||||
print(m.x)
|
||||
|
||||
print('\n')
|
||||
|
||||
print(m.y)
|
||||
|
||||
```
|
||||
|
||||
输出的结果如下:
|
||||
|
||||
```txt
|
||||
获取 name 值
|
||||
两点水
|
||||
|
||||
|
||||
设置 name 值
|
||||
获取 name 值
|
||||
三点水
|
||||
|
||||
|
||||
获取 name 值
|
||||
三点水
|
||||
|
||||
|
||||
5
|
||||
|
||||
```
|
||||
|
||||
通过这个例子,可以很好的观察到这 `__get__()` 和 `__set__()` 这些方法的调用。
|
||||
|
||||
再看一个经典的例子
|
||||
|
||||
我们知道,距离既可以用单位"米"表示,也可以用单位"英尺"表示。
|
||||
现在我们定义一个类来表示距离,它有两个属性: 米和英尺。
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
|
||||
class Meter(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 Foot(object):
|
||||
def __get__(self, instance, owner):
|
||||
return instance.meter * 3.2808
|
||||
|
||||
def __set__(self, instance, value):
|
||||
instance.meter = float(value) / 3.2808
|
||||
|
||||
|
||||
class Distance(object):
|
||||
meter = Meter()
|
||||
foot = Foot()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
d = Distance()
|
||||
print(d.meter, d.foot)
|
||||
d.meter = 1
|
||||
print(d.meter, d.foot)
|
||||
d.meter = 2
|
||||
print(d.meter, d.foot)
|
||||
|
||||
```
|
||||
|
||||
输出的结果:
|
||||
|
||||
```txt
|
||||
0.0 0.0
|
||||
1.0 3.2808
|
||||
2.0 6.5616
|
||||
```
|
||||
|
||||
在上面例子中,在还没有对 Distance 的实例赋值前, 我们认为 meter 和 foot 应该是各自类的实例对象, 但是输出却是数值。这是因为 `__get__` 发挥了作用.
|
||||
|
||||
我们只是修改了 meter ,并且将其赋值成为 int ,但 foot 也修改了。这是 `__set__` 发挥了作用.
|
||||
|
||||
描述器对象 (Meter、Foot) 不能独立存在, 它需要被另一个所有者类 (Distance) 所持有。描述器对象可以访问到其拥有者实例的属性,比如例子中 Foot 的 `instance.meter` 。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user