Python 全栈:Python 应用迭代器和生成器的 9 个案例

列表和迭代器区别

有些读者朋友,区分不开列表、字典、集合等非迭代器对象与迭代器对象,觉得迭代器是多余的。

先探讨它们的区别。首先,创建一个列表 a:

a = [1,3,5,7]

有没有朋友认为,列表就是迭代器的?注意列表 a 可不是迭代器类型(Iterator)。要想成为迭代器,需要经过内置函数 iter 包装:

a_iter = iter(a)

此时 a_iter 就是 Iterator,迭代器。可以验证:

In [21]: from collections.abc import Iterator
    ...: isinstance(a_iter,Iterator)
Out[21]: True

分别遍历 a、a_iter:

In [22]: for i in a:
    ...:     print(i)
    ...:
1
3
5
7

In [23]: for i in a_iter:
    ...:     print(i)
    ...:
1
3
5
7

打印结果一样,但是,再次遍历 a、a_iter 就会不同,a 正常打印,a_iter 没有打印出任何信息:

In [24]: for i in a:
    ...:     print(i)
    ...:
1
3
5
7

In [25]: for i in a_iter:
    ...:     print(i)
    ...:

这就是列表 a 和迭代器 a_iter 的区别:

列表不论遍历多少次,表头位置始终是第一个元素;
迭代器遍历结束后,不再指向原来的表头位置,而是为最后元素的下一个位置。
只有迭代器对象才能与内置函数 next 结合使用,next 一次,迭代器就前进一次,指向一个新的元素。

所以,要想迭代器 a_iter 重新指向 a 的表头,需要重新创建一个新的迭代 a_iter_copy

In [27]: a_iter_copy = iter(a)

调用 next,输出迭代器指向 a 的第一个元素:

In [28]: next(a_iter_copy)
Out[28]: 1

值得注意,我们无法通过调用 len 获得迭代器的长度,只能迭代到最后一个末尾元素时,才知道其长度。

那么,怎么知道迭代到元素末尾呢?我们不妨一直 next,看看会发生什么:

In [30]: next(a_iter_copy)
Out[30]: 3

In [31]: next(a_iter_copy)
Out[31]: 5

In [32]: next(a_iter_copy)
Out[32]: 7

In [33]: next(a_iter_copy)

StopIteration:
等迭代到最后一个元素后,再执行 next,会触发 StopIteration 异常。

所以,通过捕获此异常,就能求出迭代器指向 a 的长度,如下:

a = [1, 3, 5, 7]
a_iter_copy2 = iter(a)
iter_len = 0
try:
    while True:
        i = next(a_iter_copy2)
        print(i)
        iter_len += 1
except StopIteration:
    print('iterator stops')

print('length of iterator is %d' % (iter_len,))

打印结果:

1
3
5
7
iterator stops
length of iterator is 4

以上总结:遍历列表,表头位置始终不变;遍历迭代器,表头位置相应改变;next 函数执行一次,迭代对象指向就前进一次;StopIteration 触发时,意味着已到迭代器尾部。

认识到迭代器和列表等区别后,我们再来说说生成器。

yield 的函数是生成器,而生成器也是一种迭代器。所以,生成器也有上面那些迭代器的特点。前些天已经讨论过生成器的一些基本知识,今天主要讨论生成器带来哪些好处,实际的使用场景在哪里。

节省内存案例

求一组数据累积乘,比如:三个数 [1,2,3],累积乘后返回 [1,2,6]。

一般的方法:

你可能感兴趣的:(Python 全栈:Python 应用迭代器和生成器的 9 个案例)