Python面试题系列之04 迭代器、生成器

Python面试题系列之04: 什么是迭代器、生成器?

Question

什么是迭代器、生成器?

知识点详解

1 迭代器

假如我们自己写了一个数据类型,希望这个数据类型里的东西也可以使用for循环被一个一个地取出来,那我们就必须满足for的要求。这个要求就叫做“协议”。

可以被迭代要满足的要求就叫做可迭代协议。可迭代协议的定义非常简单,就是内部实现了iter方法。

1.1 什么是可迭代对象(Iterable)

如果这个对象中有__iter__()方法,这个对象就是可迭代对象。简单来说就是可以使用for...in...语句进行循环的对象。我们可以使用isinstance()方法进行判断。

>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('Pythonista', Iterable)
True
>>> isinstance((x for x in range(9)), Iterable)
True
>>> isinstance(100, Iterable)
False

1.2 什么是迭代器(Iterator)

迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代。

迭代器只不过是一个实现迭代器协议的容器对象,它基于两个方法:

  • next()方法,返回容器的下一个项目
  • iter()方法,返回迭代器本身
>>> temp = iter(['人人都是', 'Pythonista'])
>>> print(type(temp)) 
<class 'list_iterator'>
>>> print(next(temp))
人人都是
>>> print(next(temp))
Pythonista
>>> print(next(temp))
Traceback (most recent call last): StopIteration

1.3 迭代器的优势及使用场景

在构建迭代器时,不是将所有的元素一次性的加载,而是等调用next方法时返回元素,所以不需要考虑内存的问题。

所以迭代器常用于如下场景中:

  • 数列的数据规模巨大
  • 数列有规律,但是不能使用列表推导式描述。

2. 生成器

2.1 什么是生成器(Generator)

生成器类似于一种数据类型,这种数据类型自动实现了迭代器协议(其他的数据类型需要调用自己内置的__iter__方法)。生成器在Python中的表现形式分为生成器函数、生成器表达式两种。

2.1 生成器函数

生成器函数和常规函数一样,都采用def语句进行定义。但在返回结果时,生成器函数中使用的是yield语句而不是return语句。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行。

当需要一个将返回一个序列或在循环中执行的函数时,就可以使用生成器,因为当这些元素被传递到另一个函数中进行后续处理时,一次返回一个元素可以有效的提升整体性能。

2.2 生成器表达式

生成式表达式,类似于列表推导。它只是按需返回产生结果的一个对象,而不是构建一个结果列表,即按需取出对象。

生成式表达式是一种实现生成器的便捷方式,将列表推导式的中括号替换为圆括号。

>>>s = [x ** 2 for x in range(5)]
>>>s
[0, 1, 4, 9, 16]

>>>g = (x ** 2 for x in range(5))
>>>g
<generator object <genexpr> at 0x000001B670D4A408>
>>>next(g)
0
>>>next(g)
1

生成器表达式是一种边循环边计算,使得列表的元素可以在循环过程中一个个的推算出来,不需要创建完整的列表,从而节省了大量的空间。

2.3 关于生成器的总结

  • 语法上和函数类似:生成器函数和常规函数几乎是一样的。它们都是使用def语句进行定义,差别在于生成器使用yield语句返回一个值,而常规函数使用return语句返回一个值。
  • 自动实现迭代器协议:由于生成器自动实现了迭代器协议,所以我们可以调用它的next方法,并且在没有值可以返回的时候,生成器自动产生StopIteration异常。
  • 状态挂起:生成器使用yield语句返回一个值。yield语句挂起该生成器的状态,保留足够的信息,以便之后从它离开的地方继续执行。
  • 生成器只能遍历一次,不能重复使用:在程序中如果已经使用生成器遍历过一次得到结果,那么第二次遍历生成器将得不到任何结果,因为在第一次遍历生成器时,生成器已经遍历到头,它的特性决定它无法遍历第二次。

Answer

迭代器
可被next()函数调用并不断返回下一个值的对象就是迭代器。简单来说:凡是可以被for循环访问的都是迭代器,这是因为迭代器里面实现了iter协议next方法

生成器
生成器就是特殊的迭代器,它在迭代器的基础上再次进行了封装。
因为自动创建了iter()next()方法,使得生成器显得特别简洁;使用生成器表达式取代列表解析可以同时节省内存,所以生成器也很高效。
另外生成器是协程的基础,协程是通过上下文切换进行任务协作的,而生成器挂起状态的特性很好地满足了协程的需求。

后记

最后再通过这张图来理解生成器(Generator)、迭代器(Iterator)、可迭代对象(Iterable)之间关系。欢迎大家在评论区留言说出自己的理解。

好了,以上就是本篇全部内容。

备注:本篇首发于知识星球「人人都是Pythonista」。


文章作者: &娴敲棋子&
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 &娴敲棋子& !
评论
  目录