什么是生成器?
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且, 创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后 面绝大多数元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
生成器的好处是延迟计算,一次返回一个结果。也就是说,它不会一次生成所有的结果,这对于大数据量处理,将会非常有用。
1. 创建生成器方法1
要创建一个生成器,有很多种方法。第一种方法很简单,只要把一个列表生成式的[ ] 改成( )
#列表生成式
a = [x*2 for x in range(10)]
print(a)
#结果
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
#生成器
a = (x*2 for x in range(10))
print(a)
#结果
<generator object <genexpr> at 0x7f30586a3b48>
通过 next() 函数获得 generator 的下一个返回值:
a = (x*2 for x in range(3))
print(next(a))
0
print(next(a))
2
print(next(a))
4
但是当 next() 执行的次数超过范围时(因为 generator 保存的是算法),会抛出 StopIteration 异常,也就是说你把容器里的数据取完了。在去取得时候就会报错:
print(next(a))
'''
Traceback (most recent call last):
File "/root/datalysis/1.py", line 5, in <module>
print(next(a))
StopIteration
'''
因为 generator 也是可迭代对象
from collections import Iterable
# 定义一个生成器
a = (i for i in range(3))
# 判断生成器的数据是否为可迭代对象
res = isinstance(a, Iterable)
print(res)
# 返回值:True
# 说明 generator 是可迭代对象。
2.函数式生成器
generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for 循环无法实现的时候,还可以用函数来实现。
**著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到 **
#普通函数
def fib(num):
val1, val2 = 0, 1
for i in range(num):
# 这是优化写法,要消化
val1, val2 = val2, val1 + val2
print(val2)
fib(5)
1
2
3
5
8
#使用yield 关键字变成了生成器
def fib(num):
val1, val2 = 0, 1
for i in range(num):
# 这是优化写法,要消化
val1, val2 = val2, val1 + val2
yield val2
a = fib(5)
print(a)
<generator object fib at 0x7fda0158dba0>
generator 和函数的执行流程不一样,在每次执行调用 next() 时,遇到 yield 语句则返回。再次执行next()时从上次返回的 yield 语句处继续执行。
使用 for 循环迭代该生成器获取下一个返回值:
a = (i for i in range(5))
#a是生成器
for i in a:
print(i)
0
1
2
3
4
生成器的缺点
生成器被只能被遍历一次,再次调用发现没有返回值。