在 Python 中, 有这两个概念容易让人混淆. 第一个是可迭代对象(Iterable), 第二个是迭代器(Iterator), 第三个是生成器(Generator), 这里暂且不谈生成器.
可迭代对象
列表, 元组, 字符串, 字典等都是可迭代对象, 可以使用 for 循环遍历出所有元素的都可以称为可迭代对象(Iterable). 在 Python 的内置数据结构中定义了 Iterable 这个类, 在 collections.abc 模块中, 我们可以用这个来检测是否为可迭代对象
- >>> from collections import Iterable
- >>> a = [1,2,3]
- >>> isinstance(a, Iterable)
- >>> True
- >>> b = 'abcd'
- >>> isinstance(b, Iterable)
- >>> True
这些数据结构之所以能称之为 Iterable, 是因为其内部实现了__iter__()方法, 从而可迭代. 当我们使用 for 循环时, 解释器会调用内置的 iter()函数, 调用前首先会检查对象是否实现了__iter__()方法, 如果有就调用它获取一个迭代器 (接下来会讲). 加入没有__iter__() 方法, 但是实现了__getitem__()方法, 解释器会创建一个迭代器并且按顺序获取元素. 如果这两个方法都没有找到, 就会抛出 TypeError 异常. 下面我们自定义对象, 分别实现这两个方法(getitem(), iter())
- class MyObj:
- def __init__(self, iterable):
- self._iterable = list(iterable)
- def __getitem__(self, item):
- return self._iterable[item]
- obj = MyObj([1,2,3])
- for i in obj:
- print(i)
如上所示, 这里没有实现__iter__方法, 只实现了__getitem__方法, 也使得 Myobj 称为可迭代对象.
下面我们实现__iter__方法, 这里使用了 yield 语法用来产出值(这里需要生成器的知识)
- class MyObj:
- def __init__(self, iterable):
- self._iterable = list(iterable)
- def __iter__(self):
- index = 0
- while True:
- try:
- yield self._iterable[index]
- except IndexError:
- break
- index += 1
- obj = MyObj([1,2,3])
- for i in obj:
- print(i)
这里同样让对象称为可迭代对象.
迭代器
迭代器是一个可以记住遍历的位置的对象.
迭代器对象从集合的第一个元素开始访问, 直到所有的元素被访问完结束. 迭代器只能往前不会后退.
如上图所示, 迭代器 (Iterator) 继承可迭代(Iterable), 迭代器必须实现__iter__方法和__next__方法. 其中__next__方法用于产出下一个元素.
由继承图可见, 迭代器一定是可迭代对象, 可迭代对象不一定是迭代器
迭代器有两个基本的方法: iter() 和 next().
我们使用 iter(iterable)即可把可迭代对象转换成迭代器
使用 next(iterator)来获取迭代器的下一个值
- >>> a = [3,4,5]
- >>> a
- >>> [3, 4, 5]
- >>> iter(a)
- >>> <list_iterator object at 0x10b130ba8>
- >>> iterator = iter(a)
- >>> next(iterator)
- >>> 3
- >>> next(iterator)
- >>> 4
- >>> next(iterator)
- >>> 5
- >>> next(iterator)
- Traceback (most recent call last):
- File "<input>", line 1, in <module>
- StopIteration
如上所示, 因为对象实现了__next__方法, 我们可以通过 next(iterator)来获取迭代器的下一个值, 直到没有值了, 抛出 StopIteration 异常结束.
迭代器的背后
迭代器 Iterator 是一个抽象基类, 它定义在_collections_abc.py 中
Iterator 源码如下
- class Iterator(Iterable):
- __slots__ = ()
- @abstractmethod
- def __next__(self):
- 'Return the next item from the iterator. When exhausted, raise StopIteration'
- raise StopIteration
- def __iter__(self):
- return self
- @classmethod
- def __subclasshook__(cls, C):
- if cls is Iterator:
- return _check_methods(C, '__iter__', '__next__')
- return NotImplemented
可以看到, 它实现了__subclasshook__方法, 即不用显式继承 Iterator, 只需要实现__iter__和__next__方法即可称为 Iterator 的虚拟子类. 这里凸现了 Python 的鸭子类型, 实现特定的 "协议" 即可拥有某种行为.
另外, 它自己也定义了__iter__方法, 当我们使用 iter(Iterator)时直接返回自己, 不做任何处理.
iter()函数的两个用法
官方文档中给出了说明:
- iter(iterable) -> iterator
- iter(callable, sentinel) -> iterator
- Get an iterator from an object. In the first form, the argument must
- supply its own iterator, or be a sequence.
- In the second form, the callable is called until it returns the sentinel.
第一个用法: iter(iterable) -> iterator (把可迭代对象转换为迭代器)
第二个用法: iter(callable, sentinel) -> iterator (第一个参数: 任何可调用对象, 可以是函数, 第二个是标记值, 当可调用对象返回这个值时, 迭代器抛出 StopIteration 异常, 而不产出标记值)
- >>> from random import choice
- >>> values = [1,2,3,4,5,6,7]
- >>> def test_iter():
- >>> return choice(values)
- >>> it = iter(test_iter, 2)
- >>> it
- >>> <callable_iterator object at 0x10b130b00>
- >>> for i in it:
- >>> print(i)
- >>> 7
- >>> 1
- >>> 7
- >>> 3
- >>> 1
上面代码的流程: test_iter 函数从 values 列表中随机挑选一个值并返回, 调用 iter(callable, sentinel)函数, 把 sentinel 标记值设置为 2, 返回一个 callable_iterator 实例, 遍历这个特殊的迭代器, 如果函数返回标记值 2, 直接抛出异常退出程序. 这就是 iter 函数的鲜为人知的另一个用法.
来源: https://www.cnblogs.com/PyKK2019/p/11067206.html