持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情
前言
作为Python的高频面试题,面试过的都清楚,经常问到迭代器和生成器的概念和区别。那么你知道它们的区别吗?这篇文章给你答案。
迭代器
首先了解一下可迭代对象 它是实现了能返回迭代器的iter方法的对象,或者说实现了getitem方法并且它的参数是从0开始的索引。
而迭代器是一个抽象的概念,你可以将它理解是这样的对象:实现了没有参数的next方法,每次调用next方法就返回下一个元素,直到没有元素了,就抛出StopIteration 异常。而且迭代器实现了iter方法,返回迭代器本身。
以上概念说起来较抽象,我们以常见的迭代器来说,如string、dict、list等容器对象,我们都可以用for循环去获取容器的每个元素。其实for循环的过程就调用了iter()函数,而iter()函数会返回一个next()方法的迭代器对象,因此我们能遍历对象。
可迭代对象和迭代器,很多人会搞混它们的关系,这里总结一下:
- 迭代器属于可迭代对象;
- 可迭代对象拥有iter方法,而迭代器是拥有next方法;
- 定义一个可迭代对象,要实现iter方法。而定义迭代器,一定要实现iter和next方法。
迭代器代码例子
说了这么多概念,我们就用代码来看看如何创建迭代器。
- for循环遍历对象
In [2]: alist = ['a', 'b', 'c']
...: obj = iter(alist)
...: for i in obj:
...: print(i)
...:
a
b
c
例子很简单,我们用iter来创建迭代器,然后for循环来遍历。
- next方法遍历对象
In [3]: alist = ['a', 'b', 'c']
...: obj = iter(alist)
In [4]: next(obj)
Out[4]: 'a'
In [5]: next(obj)
Out[5]: 'b'
In [6]: next(obj)
Out[6]: 'c'
In [7]: next(obj)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
Input In [7], in <cell line: 1>()
----> 1 next(obj)
StopIteration:
next方法看起来较原始,每次执行,返回下一个元素。当没有元素时,我们可看到最后抛出了StopIteration异常。
生成器
说完了迭代器,我们再来看看生成器。什么是生成器呢?简单来说,生成器是用来创建迭代器的工具。我们将包含yield语句的函数都称为生成器。每次返回数据时,就会执行yield语句。执行yield后函数会暂停运行,等到下一次yield的时候从上次暂停的位置继续运行。
我们看下面例子,用yield来迭代对象,使用时我们用next方法调用下一个元素。
In [29]: def print_num():
...: for i in range(5):
...: yield i
...:
In [30]: obj = print_num()
In [31]: print(next(obj))
0
In [32]: print(next(obj))
1
In [33]: print(next(obj))
2
In [34]: print(next(obj))
3
In [35]: print(next(obj))
4
In [36]: print(next(obj))
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
Input In [36], in <cell line: 1>()
----> 1 print(next(obj))
StopIteration:
迭代器和生成器的区别
可以这样说,生成器能做到迭代器能做的所有事,而且因为自动创建了 iter()和 next()方法,生成器显得特别简洁,而且生成器也是高效的,使用生成器表达式取代列表解析可以同时节省内存。
小结
本文总结了迭代器和生成器的基本概念和使用示例,我们在很多场景下可以用到生成器,比如读取大列表时,如果一次性加载,内存压力就会很大。我们可以用生成器的方式,每次取一个元素,处理完再取下一个,这样对内存压力就不会那么大,也能提升使用效率。