[^*] 表示注脚, 在文末可以查看对应连接, 但简书不支持该语法.
迭代器是表示数据流的对象.
迭代器对象自身需要支持以下两个方法, 这两个共同构成了迭代器协议:[^7]
iterator.__iter__()
返回迭代器对象本身. 由于实现了 __iter__ 对象都是属于 IterableObjc , 因此迭代器也属于 IterableObjc; 并且在其它接受 IterableObjc 的地方, 也多半可以接受迭代器.
iterator.__next__()
返迭代器中的下一个项. 重复调用迭代器的 __next__() 方法(或通过内置函数 next() 重复调用迭代器), 将返回流中连续的项. 当没有再无数据可供使用时, 便会抛出 异常, 这时表明该迭代器对象已经耗尽, 若此后仍试图调用该迭代器的 __next__() 方法, 将会再次抛出 异常.[^4]
一旦迭代器的 __next__() 方法抛出 , 则必须在后续调用中继续抛出异常. 不遵从此特性的实现被视为不正确.[^7]
如果容器支持不同类型的迭代, 则可以提供额外的方法来专门请求这些迭代类型的迭代器(支持多种迭代形式的对象的一个例子是树结构, 它支持 breadth-first 和 depth-first 两种遍历方式).[^7]
Python 定义了多个迭代器对象, 以支持对如下类型进行迭代: 常规 (general) 序列类型和特殊 (specific) 序列类型, 字典以及其它专业表单(specialized forms). 除迭代器协议的实现之外, 其实特定类型并不重要.[^7]
1. 迭代器与 for 循环
将迭代器用于 循环时, 循环仍会调用 iter() 来处理迭代器对象, 然后通过 next() 逐一获取每个元素, 直至 __next__ 抛出 为止. 注意: 将某个迭代器对象传递给 iter() 后, 只会返回指向该迭代器的引用, 并不会创建具备新 id 的迭代器对象.
- >>> aa = [1,2,3,]
- >>> bb = iter(aa)
- >>> bb
- <list_iterator object at 0x000001B393436E48>
- >>> cc = iter(bb)
- >>> cc # bb 和 cc 引用同一个对象
- <list_iterator object at 0x000001B393436E48>
通常情况下, 如果反复尝试将某个迭代器用于 for 循环, 其实在第一次循环结束时就会耗尽该迭代器, 之后只会反复使用这个已被耗尽的迭代器, 看起来如同在使用一个空容器.[^4]
- >>> aa = [1,2,3,]
- >>> bb = iter(aa)
- >>> for i in bb:
- print(i)
- 1
- 2
- 3
- >>> for i in bb:
- print(i)
- >>>
但是, 如果在__iter__ 中重置相关变量, 则可让迭代器反复用于 for 循环, 并且每次都有输出.
- class IteratorObjc:
- def __iter__(self):
- self._count = 0
- return self
- def __next__(self):
- while self._count <3:
- self._count += 1
- return self._count
- class IteratorObjc:
- """
- 在这种情况下, 每次调用__iter__方法时,
- 会返回实例对象自身, 不会创建具备新 id 的迭代器对象.
- """
- def __iter__(self):
- # 重置_count, 便可反复进行迭代
- self._count = 0
- # 调用时返回实例自身
- return self
- def __next__(self):
- while self._count < 3:
- self._count += 1
- return self._count
- a_iterator = IterableObjc()
- for i in a_iterator:
- print(i, end=',')
- print()
- for i in a_iterator:
- print(i, end=',')
2. 迭代器与内置函数
由于迭代器也属于 IterableObjc, 所以迭代器也可直接用于如下内置函数:
- any(iterable)
- class list([iterable])
- map(function, iterable, ...)
- ....
通常情况下, 如果反复将某个迭代器传递给内置函数. 在第一次使用该迭代器时就会耗尽该迭代器, 之后只会反复使用这个已被耗尽的迭代器, 看起来如同在使用一个空容器.
- >>> aa = [1,2,3,]
- >>> bb = iter(aa)
- >>> list(bb)
- [1, 2, 3]
- >>> list(bb)
- []
但是, 如果在__iter__ 中重置相关变量, 则可让迭代器反复用于内置函数.
- class IteratorObjc:
- def __iter__(self):
- self._count = 0
- return self
- def __next__(self):
- while self._count <3:
- self._count += 1
- return self._count
- a_iterator = IterableObjc()
- print(list(a_iterator))
- print(list(a_iterator))
输出
- [1, 2, 3]
- [1, 2, 3]
- 3. itertools
- 10.1. - Functions creating iterators for efficient looping
模块提供了众多用于创建迭代器的函数. 下面简要介绍几个:
count() 函数返回的迭代器可产生一串连续的整数, 并且通过该迭代器可产生无限个整数. 与内置函数 range() 不同, count() 不需要通过参数来设定上线.
- >>> from itertools import count
- >>> counter = count(start=10)
- >>> next(counter)
- 10
- >>> next(counter)
- 11
cycle() 函数会把所接受的可迭代对象转换为一个无限循环的迭代器.
- >>> from itertools import cycle
- >>> colors = cycle(['red', 'white', 'blue'])
- >>> next(colors)
- 'red'
- >>> next(colors)
- 'white'
- >>> next(colors)
- 'blue'
- >>> next(colors)
- 'red'
islice() 函数会截取输入迭代器的一部分, 并把这部分作为输出迭代器返回
- >>> from itertools import islice
- >>> colors = cycle(['red', 'white', 'blue']) # infinite
- >>> limited = islice(colors, 0, 4) # finite
- >>> for x in limited: # so safe to use for-loop on
- ... print(x)
- red
- white
- blue
- red
注脚:
[1] 语言参考 - 3.1. Objects, values and types
[2] 标准库 8.4. - Abstract Base Classes for Containers
[3] Iterables vs. Iterators vs. Generators https://nvie.com/posts/iterators-vs-generators/ | 完全理解 Python 迭代对象, 迭代器, 生成器 http://python.jobbole.com/87805/
[4] Glossary 术语表 https://docs.python.org/3.7/glossary.html
[5] iter(object[, sentinel])
[6] 语言参考 - 8.3. The for statement
[7] 标准库 - 4.5. terator Types
[8] 语言参考 - 3.3.7. Emulating container types
[9] 语言参考 -3.2. The standard type hierarchy
来源: http://www.jianshu.com/p/a1f628eef4f1