0 前言
在讨论可迭代对象和迭代器之前,我们先来了解一下什么是迭代器:
迭代器是一种最简单也最常见的设计模式。它可以让用户透过特定的接口巡访容器中的每一个元素而不用了解底层的实现。
——《维基百科》
迭代是数据处理的基石。当内存中放不下数据集时,我们要找到一种惰性获取数据的方式,即按需一次获取一个数据项,这就是迭代器模式。
1 迭代器
1.1 可迭代和不可迭代
可迭代对象,从名称上就可以理解,它是一个可以迭代的对象。我们知道,python中的列表、元组、甚至字符串都是可以迭代的,我们通过for循环来进行迭代。那迭代是如何进行的呢?什么是可迭代的,什么又是不可迭代的呢?
当我们迭代一个对象时,解释器会对对象调用内置的iter()函数来获取一个迭代器,iter()函数有以下作用:
- 检查对象是否实现了
__iter__
方法,如果实现了就调用它,获得一个迭代器。 - 如果没有实现
__iter__
方法,但是实现了__getitem__
方法,python会创建一个迭代器,尝试按顺序(从索引0开始)获取元素。 - 如果尝试失败,python会抛出`TypeError`异常,通常会提示"C object is not iterable",其中C是目标对象所属的类。
- 获取迭代器后,可以使用迭代器的next()方法,逐次获取到对象中的每一个元素。(对迭代器使用next()方法时,如果没有元素了,抛出 StopIteration异常;但使用for循环迭代则不会抛出异常)
所以当一个对象中没有__iter__
方法时,对象是不可迭代的(not iterable)
如果一个对象定义了__iter__
方法,则它是可迭代的(iterable)
但是需要注意:__iter__
方法需要返回一个迭代器(iterator),不然不能正常的迭代。
1.2 迭代器和可迭代对象
简单理解迭代器和可迭代对象的关系,就是:迭代器是一个可迭代对象,可迭代对象不一定是迭代器,但需要包含迭代器。
我们可以自己实现一个迭代器和可迭代对象来感受一下它们的关系:
迭代器:
实现__next__方法,用以返回对象中的下一个元素;
实现__iter__
方法,返回本身(self),使自己能被迭代。
可迭代对象:
实现__iter__
方法(或者__getitem__
方法),但需要返回一个迭代器,使对象可以被迭代;
2 生成器
在Python中创建迭代器最方便的方法是使用生成器。生成器也是迭代器。生成器的语法类似于函数,但是不返回值。为了显示序列中的每一个元素,会使用yield语句。只要Python函数的定义体中有yield关键字,该函数就是生成器函数。调用生成器函数时,会返回一个生成器对象。
获取生成器通常有两种方式,生成器函数和生成器表达式。
def test(): # 生成器函数
for i in range(10):
yield i
r = (i for i in range(10)) # 生成器表达式
t = test()
print(t) # <generator object test at 0x0000015BB3126B48>
print(r) # <generator object <genexpr> at 0x0000015BB318C548>
print('__next__' in dir(t)) # True
print('__next__' in dir(r)) # True
由以上可以看出,生成器也是一种迭代器。