|
| 1 | +迭代器 (Iterator) |
| 2 | +==== |
| 3 | + |
| 4 | +# 迭代和可迭代 |
| 5 | + |
| 6 | +迭代器这个概念在很多语言中(比如 C++,Java)都是存在的,但是不同语言实现迭代器的方式各不相同。**在 Python 中,迭代器是指遵循迭代器协议(iterator protocol)的对象。**至于什么是迭代器协议,稍后自然会说明。为了更好地理解迭代器,我先介绍和迭代器相关的两个概念: |
| 7 | + |
| 8 | +- 迭代(Iteration) |
| 9 | +- 可迭代对象(Iterable) |
| 10 | + |
| 11 | +你可能会觉得这是在玩文字游戏,但这确实是要搞清楚的。 |
| 12 | + |
| 13 | +> 当我们用一个循环(比如 for 循环)来遍历容器(比如列表,元组)中的元素时,这种遍历的过程就叫***迭代***。 |
| 14 | +
|
| 15 | +在 Python 中,我们使用 `for...in...` 进行迭代。比如,遍历一个 list: |
| 16 | + |
| 17 | +```python |
| 18 | +numbers = [1, 2, 3, 4] |
| 19 | +for num in numbers: |
| 20 | + print num |
| 21 | +``` |
| 22 | + |
| 23 | +像上面这种可以使用 `for` 循环进行迭代的对象,就是可迭代对象,它的定义如下: |
| 24 | + |
| 25 | +> 含有 `__iter__()` 方法或 `__getitem__()` 方法的对象称之为***可迭代对象***。 |
| 26 | +
|
| 27 | +我们可以使用 Python 内置的 `hasattr()` 函数来判断一个对象是不是可迭代的: |
| 28 | + |
| 29 | +```python |
| 30 | +>>> hasattr((), '__iter__') |
| 31 | +True |
| 32 | +>>> hasattr([], '__iter__') |
| 33 | +True |
| 34 | +>>> hasattr({}, '__iter__') |
| 35 | +True |
| 36 | +>>> hasattr(123, '__iter__') |
| 37 | +False |
| 38 | +>>> hasattr('abc', '__iter__') |
| 39 | +False |
| 40 | +>>> hasattr('abc', '__getitem__') |
| 41 | +True |
| 42 | +``` |
| 43 | + |
| 44 | +另外,我们也可使用 `isinstance()` 进行判断: |
| 45 | + |
| 46 | +```python |
| 47 | +>>> from collections import Iterable |
| 48 | + |
| 49 | +>>> isinstance((), Iterable) # 元组 |
| 50 | +True |
| 51 | +>>> isinstance([], Iterable) # 列表 |
| 52 | +True |
| 53 | +>>> isinstance({}, Iterable) # 字典 |
| 54 | +True |
| 55 | +>>> isinstance('abc', Iterable) # 字符串 |
| 56 | +True |
| 57 | +>>> isinstance(100, Iterable) # 数字 |
| 58 | +False |
| 59 | +``` |
| 60 | + |
| 61 | +可见,我们熟知的字典(dict)、元组(tuple)、集合(set)和字符串对象都是可迭代的。 |
| 62 | + |
1 | 63 | # 迭代器 |
2 | 64 |
|
| 65 | +现在,让我们看看什么是迭代器(Iterator)。上文说过,**迭代器是指遵循迭代器协议(iterator protocol)的对象。**从这句话我们可以知道,迭代器是一个对象,但比较特别,它需要遵循迭代器协议,那什么是迭代器协议呢? |
| 66 | + |
| 67 | +> ***迭代器协议(iterator protocol)***是指要实现对象的 `__iter()__` 和 `next()` 方法(注意:Python3 要实现 `__next__()` 方法),其中,`__iter()__` 方法返回迭代器对象本身,`next()` 方法返回容器的下一个元素,在没有后续元素时抛出 `StopIteration` 异常。 |
| 68 | +
|
| 69 | +接下来讲讲迭代器的例子,有什么常见的迭代器呢?列表是迭代器吗?字典是迭代器吗?我们使用 `hasattr()` 进行判断: |
| 70 | + |
| 71 | +``` |
| 72 | +>>> hasattr((1, 2, 3), '__iter__') |
| 73 | +True |
| 74 | +>>> hasattr((1, 2, 3), 'next') # 有 __iter__ 方法但是没有 next 方法,不是迭代器 |
| 75 | +False |
| 76 | +>>> |
| 77 | +>>> hasattr([1, 2, 3], '__iter__') |
| 78 | +True |
| 79 | +>>> hasattr([1, 2, 3], 'next') |
| 80 | +False |
| 81 | +>>> |
| 82 | +>>> hasattr({'a': 1, 'b': 2}, '__iter__') |
| 83 | +True |
| 84 | +>>> hasattr({'a': 1, 'b': 2}, 'next') |
| 85 | +False |
| 86 | +``` |
| 87 | + |
| 88 | +同样,我们也可以使用 `isinstance()` 进行判断: |
| 89 | + |
| 90 | +``` |
| 91 | +>>> from collections import Iterator |
| 92 | +>>> |
| 93 | +>>> isinstance((), Iterator) |
| 94 | +False |
| 95 | +>>> isinstance([], Iterator) |
| 96 | +False |
| 97 | +>>> isinstance({}, Iterator) |
| 98 | +False |
| 99 | +>>> isinstance('', Iterator) |
| 100 | +False |
| 101 | +>>> isinstance(123, Iterator) |
| 102 | +False |
| 103 | +``` |
| 104 | + |
| 105 | +可见,**虽然元组、列表和字典等对象是可迭代的,但它们却不是迭代器!**对于这些可迭代对象,可以使用 Python 内置的 `iter()` 函数获得它们的迭代器对象,看下面的使用: |
| 106 | + |
| 107 | +``` |
| 108 | +>>> from collections import Iterator |
| 109 | +>>> isinstance(iter([1, 2, 3]), Iterator) # 使用 iter() 函数,获得迭代器对象 |
| 110 | +True |
| 111 | +>>> isinstance(iter('abc'), Iterator) |
| 112 | +True |
| 113 | +>>> |
| 114 | +>>> my_str = 'abc' |
| 115 | +>>> next(my_str) # my_str 不是迭代器,不能使用 next(),因此出错 |
| 116 | +--------------------------------------------------------------------------- |
| 117 | +TypeError Traceback (most recent call last) |
| 118 | +<ipython-input-15-5f369cd8082f> in <module>() |
| 119 | +----> 1 next(my_str) |
| 120 | +
|
| 121 | +TypeError: str object is not an iterator |
| 122 | +>>> |
| 123 | +>>> my_iter = iter(my_str) # 获得迭代器对象 |
| 124 | +>>> isinstance(my_iter, Iterator) |
| 125 | +True |
| 126 | +>>> next(my_iter) # 可使用内置的 next() 函数获得下一个元素 |
| 127 | +'a' |
| 128 | +``` |
| 129 | + |
| 130 | +事实上,Python 的 `for` 循环就是先通过内置函数 `iter()` 获得一个迭代器,然后再不断调用 `next()` 函数实现的,比如: |
| 131 | + |
| 132 | +``` |
| 133 | +for x in [1, 2, 3]: |
| 134 | + print i |
| 135 | +``` |
| 136 | + |
| 137 | +等价于 |
| 138 | + |
| 139 | +```python |
| 140 | +# 获得 Iterator 对象 |
| 141 | +it = iter([1, 2, 3]) |
| 142 | + |
| 143 | +# 循环 |
| 144 | +while True: |
| 145 | + try: |
| 146 | + # 获得下一个值 |
| 147 | + x = next(it) |
| 148 | + print x |
| 149 | + except StopIteration: |
| 150 | + # 没有后续元素,退出循环 |
| 151 | + break |
| 152 | +``` |
| 153 | + |
| 154 | +# 斐波那契数列迭代器 |
| 155 | + |
| 156 | +现在,让我们来自定义一个迭代器:斐波那契(Fibonacci)数列迭代器。根据迭代器的定义,我们需要实现 `__iter()__` 和 `next()` 方法(在 Python3 中是 `__next__()` 方法)。先看代码: |
| 157 | + |
| 158 | +```python |
| 159 | +# -*- coding: utf-8 -*- |
| 160 | + |
| 161 | +from collections import Iterator |
| 162 | + |
| 163 | +class Fib(object): |
| 164 | + def __init__(self): |
| 165 | + self.a, self.b = 0, 1 |
| 166 | + |
| 167 | + # 返回迭代器对象本身 |
| 168 | + def __iter__(self): |
| 169 | + return self |
| 170 | + |
| 171 | + # 返回容器下一个元素 |
| 172 | + def next(self): |
| 173 | + self.a, self.b = self.b, self.a + self.b |
| 174 | + return self.a |
| 175 | + |
| 176 | +def main(): |
| 177 | + fib = Fib() # fib 是一个迭代器 |
| 178 | + print 'isinstance(fib, Iterator): ', isinstance(fib, Iterator) |
| 179 | + |
| 180 | + for i in fib: |
| 181 | + if i > 10: |
| 182 | + break |
| 183 | + print i |
| 184 | + |
| 185 | +if __name__ == '__main__': |
| 186 | + main() |
| 187 | +``` |
| 188 | + |
| 189 | +在上面的代码中,我们定义了一个 Fib 类,用于生成 Fibonacci 数列。在类的实现中,我们定义了 `__iter__` 方法,它返回对象本身,这个方法会在遍历时被 Python 内置的 `iter()` 函数调用,返回一个迭代器。类中的 `next()` 方法用于返回容器的下一个元素,当使用 `for` 循环进行遍历的时候,就会使用 Python 内置的 `next()` 函数调用对象的 `next` 方法(在 Python3 中是 `__next__` 方法)对迭代器进行遍历。 |
| 190 | + |
| 191 | +运行上面的代码,可得到如下结果: |
| 192 | + |
| 193 | +``` |
| 194 | +isinstance(fib, Iterator): True |
| 195 | +1 |
| 196 | +1 |
| 197 | +2 |
| 198 | +3 |
| 199 | +5 |
| 200 | +8 |
| 201 | +``` |
| 202 | + |
| 203 | +# 小结 |
| 204 | + |
| 205 | +- 元组、列表、字典和字符串对象是可迭代的,但不是迭代器,不过我们可以通过 `iter()` 函数获得一个迭代器对象; |
| 206 | +- Python 的 `for` 循环实质上是先通过内置函数 `iter()` 获得一个迭代器,然后再不断调用 `next()` 函数实现的; |
| 207 | +- 自定义迭代器需要实现对象的 `__iter()__` 和 `next()` 方法(注意:Python3 要实现 `__next__()` 方法),其中,`__iter()__` 方法返回迭代器对象本身,`next()` 方法返回容器的下一个元素,在没有后续元素时抛出 `StopIteration` 异常。 |
| 208 | + |
| 209 | +# 参考资料 |
| 210 | + |
| 211 | +- [Callback or Iterator in Python](https://code-maven.com/callback-or-iterator-in-python) |
| 212 | +- [迭代器 - 廖雪峰的官方网站](http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143178254193589df9c612d2449618ea460e7a672a366000) |
| 213 | + |
| 214 | + |
0 commit comments