什么是生成器?
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且, 创建一个包含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
生成器的缺点
生成器被只能被遍历一次,再次调用发现没有返回值。