可迭代对象
简单来讲,一个实际保存的序列,或者可以通过类似for i in [1, 2, 3, 4, 5]:
类似的语法取值的对象,列表就是一个典型的可迭代对象:
>>> my_list = [1, 2, 3, 4, 5]
>>> for i in my_list:
... print(i)
...
1
2
3
4
5
for
语句在此处提供了一个迭代环境,for
语句通过跌打协议从可迭代对象中取出值。
for
循环执行时,实际上是获取了可迭代对象的迭代器,调用了迭代器的__next__
函数,来生成序列的下一个值。当序列结束时,调用__next__
函数会抛出StopIteration
异常,for
循环语句可以捕获这个异常,以决定何时终止循环。
注意,可迭代对象是不可以直接调用
__next__()
方法的。
>>> my_list.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__next__'
迭代器
通常,我们可以通过iter()
方法获取可迭代对象中的迭代器,迭代器可以直接调用__next__()
方法:
>>> li = iter(my_list)
>>> li.__next__()
1
>>> iter_list = iter(my_list)
>>> iter_list.__next__()
1
>>> iter_list.__next__()
2
>>> iter_list.__next__()
3
>>> iter_list.__next__()
4
>>> iter_list.__next__()
5
>>> iter_list.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
当迭代器结束时,调用__next__()
方法会抛出StopIteration
异常。
在Pyhon3中,还提供了一个内置函数next()
,可以自动调用一个对象的__next__()
方法。
>>> iter_list = iter(my_list)
>>> next(iter_list)
1
>>> next(iter_list)
2
>>> next(iter_list)
3
>>> next(iter_list)
4
>>> next(iter_list)
5
>>> next(iter_list)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
同样地,内置
next()
方法也不可以直接用于可迭代对象。
>>> next(my_list)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'list' object is not an iterator
报错显示list
对象不是一个迭代器。
生成器
生成器提供了一个在需要的时候才产生结果的工具,而不是立即产生结果,可以通过两种方式来创建
- 生成器函数
例如:
def squares(n):
for i in range(n):
yield = i ** 2
yield
语句会挂起该函数,并向调用者发送一个值,并且保存函数当前的执行状态,类似于上下文,使得函数能够在离开的地方继续执行。
当函数包括一条yield
语句时, 会自动编译称为生成器,当调用时,会返回一个生成器对象,该对象支持使用__next__()
方法来自动执行接口,并且在结束时抛出StopIteration
异常
- 生成器表达式
生成器表达式与列表解析表达式十分相似
>>> [x ** 2 for x in range(4)]
[0, 1, 4, 9]
上面的语句是一个列表解析表达式,返回一个列表。
>>> (x ** 2 for x in range(4))
<generator object <genexpr> at 0x1030cfe60>
将中括号改为小括号,这条语句就变成了一个生成器表达式,如果想保存并且使用该表达式,需要使用一个变量接收。
>>> x = (x ** 2 for x in range(4))
>>> x.__next__()
0
>>> next(x)
1
>>> x.__next__()
4
>>> next(x)
9
>>> x.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
- 简单来讲,生成器可以认为是对内存的优化,不是一次性产生所有结果, 而是将循环过程分散到了每一次调用,在处理非常大的结果集合运算时,是最优的选择。