在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状态挂起该生成器函数的状态,保留足够的信息,以便之后从它离开的地方继续执行,这就为我们之后文章会分析到的协程的实现提供了简单的实现思路。
全栈公众号,感兴趣的小伙伴可以关注下