描述符(Property的原理)
所谓描述符(descriptor) 就是将某种特殊类型的类的实例指派给另一个类的属性。
那什么是特殊类型的类呢?就是至少要在这个类里边定义_ _get_ _()、_ _set_ _()或_ _delete_ _()三个特殊方法中的任意一个。
下表列举了描述符相关的魔法方法。
魔法方法 | 含义 |
---|---|
_ _get_ _(self, instance, owner) | 用于访问属性,它返回属性的值 |
_ _set_ _(self, instance, value) | 将在属性分配操作中调用,不返回任何内容 |
_ _delete_ _(self, instance) | 控制删除操作,不返回任何内容 |
举个简单的例子:
由于MyDescriptor实现了_ _get_ _()、_ _set_ _()和_ _delete_ _()方法,并将它的类实例指派给Test类的属性,所以MyDescriptor就是所谓描述符类。
1、_ _get_ _()方法
当访问x属性的时候,Python会自动调用描述符的_ _get_ _()方法,几个参数的内容分别是:self是描述符自身的实例;instance是这个描述符的拥有者所在的类的实例,在这里也就是Test类的实例;owner是这个描述符的拥有者所在的类本身。
2、_ _set_ _()方法
对x属性进行赋值操作的时候,Python会自动调用_ _set_ _()方法,前两个参数跟_ _get_ _()方法是一样的,最后一个参数value是等号右边的值。
3、最后一个del操作也是同样的道理:
只要弄清楚描述符,那么property就不再是秘密了。property实际上就是一个描述符类。下面定义一个属于我们自己的MyProperty:
定制序列
本节要谈论的是定制容器,要向成功的实现容器的定制,便需要先谈一谈协议。
所谓协议(Protocol),其与其他编程语言中的接口很相似,它规定哪些方法必须要定义。在Python中协议并没有那么正式,事实上,在Python中,协议更像是一种指南。
在Python中,像 序列类型(例如元组、列表、字符串) 或 映射类型(如字典) 都是属于容器类型。要定制容器,就要先知道定制容器有关的一些协议:
- 如果你希望定制的容器是不可变的话,你只需要定义_ _len_ _()和_ _getitem_ _()方法。
- 如果你希望定制的容器是可变的话,除了_ _len_ _()和_ _getitem_ _()方法,你还需要定义_ _setitem_ _()和_ _delitem_ _()两个方法。
下表列举了定制容器类型相关的魔法方法及含义:
魔法方法 | 含义 |
---|---|
_ _len_ _(self) | 定义当被len()函数调用时的行为(返回容器中元素的个数) |
_ _getitem_ _(self, key) | 定义获取容器中指定元素的行为,相当于self[key] |
_ _setitem_ _(self, key, item) | 定义设置容器中指定元素的行为,相当于self[key] = value |
_ _delitem_ _(self, key) | 定义删除容器中指定元素的行为,相当于del self[key] |
_ _iter_ _(self) | 定义当迭代容器中的元素的行为 |
_ _reversed_ _(self) | 定义当被reversed()函数调用时的行为 |
_ _contains_ _(self, item) | 定义当使用成员测试运算符(in或not in)时的行为 |
举个例子,定义一个不可改变的自定义列表,要求记录列表中每个元素被访问的次数:
迭代器
迭代相信大家已经遇到过很多次了,迭代的意思类似于循环,每一次重复的过程被称为一次迭代的过程,而每一次迭代得到的结果会被用来作为下一次迭代的初始值。
提供迭代方法的容器称为迭代器,通常接触的迭代器有序列(列表、元组、字符串)还有字典,它们都支持迭代的操作。
举个例子,通常使用for循环语句来进行迭代:
字符串就是一个容器,同时也是一个迭代器,for循环语句的作用就是触发这个迭代器的迭代功能,每次从容器中依次拿出一个数据,这就是迭代操作。同样,字典和文件也是支持迭代操作的。
关于迭代,Python提供了两个BIF:
- iter()
- next()
对于一个容器对象,调用 iter() 就得到它的迭代器,调用 next() 迭代器就会返回下一个值,然后怎么结束呢?如果迭代器没有值可以返回了,就会抛出一个叫做StopIteration的异常:
那么关于实现迭代器的魔法方法有两个:
- _ _iter_ _()
- _ _next_ _()
一个容器如果是迭代器,就必须要实现_ _iter_ _()魔法方法,这个方法实际上就是返回迭代器本身。接下来重点要实现的是_ _next_ _()魔法方法,因为它决定了迭代的规则。
举个简单的例子: