什么是生成器
通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的,而且创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间,在Python中,这种一边循环一边计算的机制,称为生成器:generator
生成器是一个特殊的程序,可以被用作控制循环的迭代行为,python中生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器。
生成器类似于返回值为数组的一个函数,这个函数可以接受参数,可以被调用,但是,不同于一般的函数会一次性返回包括了所有数值的数组,生成器一次只能产生一个值,这样消耗的内存数量将大大减小,而且允许调用函数可以很快的处理前几个返回值,因此生成器看起来像是一个函数,但是表现得却像是迭代器
如何创建生成器
方法一
将列表推导式中的[]换成()即可得到一个生成器。
g = (i for i in range(10))
print(g)
>>> <generator object <genexpr> at 0x0000022C5A64B138>
g是一个生成器,可以产生数字0~9。
方法二
在一个普通函数中使用 yield 关键字。
引例
实现一个函数,返回字符串里每个单词的首字母所对应的下标
方法一
用一个列表保存字符串里每个单词的首字母所对应的下标,然后返回这个列表即可
def index_words(text):
result = []
if text:
result.append(0)
for idx, letter in enumerate(text):
if letter == ' ':
result.append(idx + 1)
return result
address = 'Four score and seven years ago...'
re = index_words(address)
输出: [0, 5, 11, 15, 21, 27]
方法二
使用生成器函数
def index_word_iter(text):
if text:
yield 0
for idx, letter in enumerate(text):
if letter == ' ':
yield idx + 1
address = 'Four score and seven years ago...'
re = list(index_word_iter(address))
输出: [0, 5, 11, 15, 21, 27]
生成器的工作原理
(1)生成器(generator)能够迭代的关键是它有一个next()方法,
工作原理就是通过重复调用next()方法,直到捕获一个异常。
(2)带有 yield 的函数不再是一个普通函数,而是一个生成器generator。
可用next()调用生成器对象来取值。next 两种方式 t.__next__() | next(t)。
可用for 循环获取返回值(每执行一次,取生成器里面一个值)
(基本上不会用next()来获取下一个返回值,而是直接使用for循环来迭代)。
(3)yield相当于 return 返回一个值,并且记住这个返回的位置,下次迭代时,代码从yield的下一条语句开始执行。
(4)send() 和next()一样,都能让生成器继续往下走一步(下次遇到yield停),但send()能传一个值,这个值作为yield表达式整体的结果
——换句话说,就是send可以强行修改上一个yield表达式值。比如函数中有一个yield赋值,a = yield 5,第一次迭代到这里会返回5,a还没有赋值。第二次迭代时,使用.send(10),那么,就是强行修改yield 5表达式的值为10,本来是5的,那么a=10。
send()使用实例
用生成器实现一个斐波那契数列,先不使用send(),观察程序的输出结果。
"""
用生成器实现斐波那契数列
"""
import sys
def fibonacci(n):
first, second, counter = 0, 1, 0
while True:
if counter >= n:
return
yield first
first, second = second, first + second
counter += 1
if __name__ == "__main__":
fb = fibonacci(5)
while True:
try:
print(next(fb))
except StopIteration:
sys.exit()
程序输出如下:
0
1
1
2
3
输出解释:程序的功能是输出斐波那契数列的前5个元素。从主函数开始执行,遇到第一个print(next(fb))语句时,会进入fionacci()函数中,并且执行到yield first语句便停止,将first返回,此时first的值是0。(注意yield first语句后面的程序不会被执行)。当主函数执行到下一个print(next(fb))语句时,fionacci()函数将从上次暂停的位置继续往下执行,执行到下一个yield first语句再次停止。
我们使用send()语句使yield表达式有一个返回结果,代码修改为:
import sys
def fibonacci(n):
first, second, counter = 0, 1, 0
while True:
if counter >= n:
return
a = yield first
print('counter: ', counter, 'a: ', a)
first, second = second, first + second
counter += 1
if __name__ == "__main__":
fb = fibonacci(5)
while True:
try:
print(next(fb))
fb.send(10)
except StopIteration:
sys.exit()
输出:
0
counter: 0 a: 10
counter: 1 a: None
1
counter: 2 a: 10
counter: 3 a: None
3
counter: 4 a: 10
程序只输出了数列中的3个元素,并不是5个,但不是因为程序执行发生了错误,注意send()的功能,send() 和next()一样,都能让生成器继续往下走一步(下次遇到yield停)。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具