Python3 生成器解析
第6章 函数
- 6.1 函数的定义和调用
- 6.2 参数传递
- 6.3 函数返回值
- 6.4 变量作用域
- 6.5 匿名函数(lambda)
- 6.6 递归函数
- 6.7 迭代器
- 6.8 生成器
- 6.9 装饰器
6.8 生成器
看看廖雪峰大神的解释:
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。
而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?
这样就不必创建完整的list,从而节省大量的空间。
在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。
生成器generator,也是一种迭代器,但是你只能对其迭代一次。这是因为它们并没有把所有的值存在内存中,而是在运行时生成值。
生成器generator对象是一种特殊的迭代器iterator函数,它会在执行过程中保存执行的上下文环境,并在下次循环中从yield语句后继续执行,生成器的标志就是yield关键字。
generator不需要抛出StopIteration异常(你可以看做yield已经在内部实现了StopIteration跳出循环),函数并没有将序列项一次生成,所以generator在实现上可以有无穷个元素,而不需要无穷的存储空间,这在内存优化方面很有用处。
使用isinstance(实体名,Generator)可判断是否为生成器。
# 验证下一个列表是否为可迭代对象Iterable、迭代器Iterator、生成器Generator
from collections.abc import Iterator, Iterable, Generator
province = ['Guangdong', 'HuNan', 'JiangSu', 'HeNan', 'HeBei']
print(isinstance(province, Iterator), isinstance(province, Iterable), isinstance(province, Generator))
output:
False True False
# 从结果来看,一个列表是可迭代对象但不是迭代器,也不是生成器
你通过遍历来使用它们,要么用一个for循环,要么将它们传递给任意可以进行迭代的函数和结构。大多数时候生成器是以函数来实现的。然而,它们并不返回一个值,而是yield(暂且译作“生出”)一个值。
生成器的创建办法有两种:
- 通过函数创建,称作生成器函数generator function
- 通过推导式创建,例如g=(x*2 for x in range(10)),称作生成器表达式generator expression
每次对生成器调用 next() 时,它会从上次离开位置恢复执行(它会记住上次执行语句时的所有数据值)。显示如何非常容易地创建生成器的示例如下:
def reverse(data):
for index in range(len(data)-1, -1, -1):
yield data[index]
>>> for char in reverse('golf'):
... print(char)
...
f
l
o
g
可以用生成器来完成的操作同样可以用前一节所描述的基于类的迭代器来完成。但生成器的写法更为紧凑,因为它会自动创建 iter() 和 next()方法。
生成器表达式generator expression
生成器不一定要用复杂的函数表示,python提供了简洁的生成器表达式。
从形式上来看,生成器表达式和列表推导式很像,仅仅是将列表推导式中的[]替换为(),但是两者差别挺大,生成器表达式可以说组合了迭代功能和列表解析功能。
生成器表达式可以认为是一种特殊的生成器函数,类似于lambda表达式和普通函数。但是和生成器一样,生成器表达式也是返回生成器generator对象,一次只返回一个值。
# 生成器表达式
g = (x*2 for x in range(4))
print(type(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
f = (i*i for i in range(10))
print(type(f))
print(next(f))
print(next(f))
print(next(f))
print(next(f))
output:
<class 'generator'>
0
2
4
6
<class 'generator'>
0
1
4
9