什么是生成器?

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,  创建一个包含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

生成器的缺点

生成器被只能被遍历一次,再次调用发现没有返回值。