1. 什么是generator
列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的,如果创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
如果可以先不创建完整的list,而是先创建一个东西,使得这个东西可以在循环的过程中计算出后面的元素,这样可以节省大量的空间。这个一边循环一边计算的机制,成为生成器:generator
2. 创建generator
<1> 第一种方法:把列表生成式的[]改成()
这里可以看到,函数gen_ner只是把gen_list最外层的[]改成了(),结果就是L是一个list,而g是一个generator
1 | def gen_list(): |
从上面的结果可以看到,generator是一个对象,如何打印出放在里面的元素呢,可以一个一个用next()打印,不过用next的话有一个缺点是,需要不断地调用next去计算g的下一个元素(next(g)),这种方法不能指定需要的那个元素,而且在没有更多元素的时候,会抛出StopIteration的错误。
那么更便捷的方法是什么呢?用for循环打印:
1 | def gen_ner(): |
2.1 例子(斐波拉契数列)
著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:
1, 1, 2, 3, 5, 8, 13, 21, 34, …
1 | def fib(max): |
1 | 执行结果是: |
其中:a, b = b, a + b为赋值语句,相当于:
1 | t = (b, a + b) # t是一个tuple |
但不必显式写出临时变量t就可以赋值,也就是说,a, b = b, a + b这句话的意思是:把a赋值给b,把b赋值给a+b。t只是作为一个中间变量出现,不用中间变量的时候可以直接赋值。

2.2 斐波拉契数列类似generator
斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator,也就是”2.1 例子(斐波拉契数列)“的函数和generator仅一步之遥。要把fib函数变成generator函数,只需要把print(b)改为yield b就可以了:
1 | def fib(max): |
1 | 运行的结果是: |
3. generator函数的执行顺序
普通函数是顺序执行,遇到return语句或者最后一行函数语句就返回。
generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行,例子如下:
先定义一个generator函数,依次返回数字1,3,5:
1 | def odd(): |
调用该generator函数时,首先要生成一个generator对象,然后用next()函数不断获得下一个返回值:
1 | o = odd() |
可以看到,odd不是普通函数,而是generator函数,在执行过程中,遇到yield就中断,下次又继续执行。执行3次yield后,已经没有yield可以执行了,所以,第4次调用next(o)就报错。
我们在循环过程中不断调用yield,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。
同样的,把函数改成generator函数后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代:
1 | def odd(): |
1 | 结果是: |
但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:
1 | def odd(): |