10分钟读懂python生成器

216 阅读3分钟

在python中,生成器在项目中的使用频率不可谓不高,但是对于刚接触python的开发者来说,又是一个比较难懂的概念。本文将以一种简单易懂的方式来解释什么是python生成器。

列表生成式 vs 生成器

列表生成式和生成器经常被拿来做比较,一个比较直观的对比是:

  • 列表生成式:[ ]是通过遍历可迭代对象生成一个list

  • 生成器:( )是直接返回可迭代对象

举个列表生成器的例子🌰:

[x+y for x in range(1,2) for y in range(1,3)] 
# result: [2,3,4,3,4,5]

通过外层[ ]包裹内层的循环,从range(1,2)列表和range(1,3)中分别取出x和y作相加操作,返回一个列表生成式,直接获得列表结果:[2,3,4,3,4,5]。这就是一个列表生成器。

在这里,我们不妨让range(1,2)提升几个量级到到range(1,20000),让range(1,3)提升几个量级到到range(1,30000),再次计算结果就是一个容量为20000*30000的列表,占用了很多的存储空间。假如我们需要得到列表的第1000个元素,那第1000个后面的元素就完全不需要用到却占用空间,有点浪费。要是能够通过某种方式,不用计算全部的结果就能得到结果,那该多好了。

于是我们的生成器登场了。

将上面的[ ]改为( )就是生成器了☑️:

g = (x+y for x in range(1,2) for y in range(1,3)])
for i in g:
    print (i)

我们创建了一个名为g的generator后,而是通过for循环来迭代打印它(其内部是通过不停调用next( )方法每次迭代出一个数据)

生成器函数

生成器函数和普通函数在语法上几乎一样,但是生成器函数多了个yield关键字。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator

 def gen_list(n):
    for i in range(n):
      yield i ** 2
 
g = gen_list(4);
print(next(g));
print(next(g));
print(next(g));
# 结果
# 0
# 1
# 4

这里通过yield语句一次返回一个结果并挂起当前状态,下次调用的时候会从当前状态继续。通过这种方式,对延迟操作提供了支持。所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果。就像这个例子中,我们每次调用next,每次只得到一个结果(依次是0、1、4...)

对比前面的通过列表生成式提高量级到range(1,20000)的例子来说,这里的生成器因为一个一个结果计算,不需要计算全部列表,所以极大地节省了空间。同时,生成器使用通过yield状态挂起该生成器函数的状态,保留足够的信息,以便之后从它离开的地方继续执行,这就为我们之后文章会分析到的协程的实现提供了简单的实现思路。

全栈公众号,感兴趣的小伙伴可以关注下

你的全栈助手