随笔 - 214  文章 - 12  评论 - 40  阅读 - 38万

Python进阶08 生成器

一、生成器

通过列表推导,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们就不必创建完整的list,从而节省大量的空间。
在Python中,这种一边循环一边计算的机制,称为生成器:generator。

二、生成器方法一

第一种方法很简单,只要把一个列表推导的[]改成(),就创建了一个generator:

>>> L = [x * x for x in range(5)]
>>> L
[0, 1, 4, 9, 16]
>>> g = (x * x for x in range(5))
>>> g
<generator object <genexpr> at 0x1044299a8>

创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。

我们可以通过next()函数获得generator的下一个返回值,从而获得generator的每一个元素。

复制代码
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
复制代码

 

generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。
当然,遍历generator正确的方法是使用for循环,因为generator也是可迭代对象:

复制代码
>>> g = (x * x for x in range(5))
>>> for x in g:
...     print(x)
...
0
1
4
9
16
复制代码

我们创建了一个generator后,基本上不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。

三、生成器方法二

如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'
>>> f = fib(6)
>>> f
<generator object fib at 0x1044299a8>

 

这里,比较难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

复制代码
>>> next(f)
1
>>> next(f)
1
>>> next(f)
2
>>> next(f)
3
>>> next(f)
5
>>> next(f)
8
>>> next(f)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration: done
复制代码

 

实际上,把函数改成generator后,我们也基本上不会用next()来获取下一个返回值,而是直接使用for循环来迭代:

复制代码
>>> for n in fib(6):
...     print(n)
...
1
1
2
3
5
8
复制代码

四、总结

generator是非常强大的工具,在Python中,可以简单地把列表推导改成generator,也可以通过函数实现复杂逻辑的generator。
对于列表推导改成的generator来说,它是在for循环的过程中不断计算出下一个元素,并在适当的条件结束for循环。
对于函数改成的generator来说,遇到return语句或者执行到函数体最后一行语句,就是结束generator的指令,for循环随之结束。

请注意区分普通函数和generator函数,普通函数调用直接返回结果:

>>> r = abs(6)
>>> r
6

generator函数的“调用”实际返回一个generator对象:

>>> f = fib(6)
>>> f
<generator object fib at 0x104429a20>

 

参考文章(这篇文章比书上讲的更通俗易懂):

https://www.liaoxuefeng.com/wiki/1016959663602400/1017318207388128

posted on   麦克煎蛋  阅读(189)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示