你知道Python迭代器和生成器的区别吗

422 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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()方法,生成器显得特别简洁,而且生成器也是高效的,使用生成器表达式取代列表解析可以同时节省内存。

小结

本文总结了迭代器和生成器的基本概念和使用示例,我们在很多场景下可以用到生成器,比如读取大列表时,如果一次性加载,内存压力就会很大。我们可以用生成器的方式,每次取一个元素,处理完再取下一个,这样对内存压力就不会那么大,也能提升使用效率。