那么, 在特定的情境当中, 存在着这样一种极为神奇的函数, 它就是生成器函数, 它呢, 既好像函数, 又仿佛是一个能够记住中间状态的魔法盒子 , 在处理大数据流之际, 它的好用程度尤为显著!
这篇文章, 会一步步引领你弄明白, 生成器函数究竟是什么东西, 有什么作用, 该如何去写, 又该怎么加以使用。
参考文章: 迭代器 | 简单一点学习 .me
生成器函数究竟是什么, 普通函数和生成器函数有何不同, yield到底是什么, 该怎么使用, 用for去遍历生成器函数时yield与之有什么区别, 用next()手动调用生成器会怎样, yield from如何更优雅地嵌套生成器, 生成器函数有哪些应用场景, 有什么需要注意的事项以及陷阱, 小结和写法建议又是什么, 什么是生成器函数。
生成器函数, 是一种借助yield却并非使用返回值的函数。其发挥的作用在于, 每一次进行调用时会返回一个值, 然而并不会退出该函数, 而是会在当下位置暂停, 下一次会继续从暂停的地方开始执行。
举个栗子:
def my_gen(): yield 1 yield 2 yield 3调用这个函数时,它不会立马执行,而是返回一个生成器对象:
g = my_gen() print(g) #2. 普通函数 vs 生成器函数特性普通函数生成器函数
使用关键字
yield
返回什么
一次性返回所有结果
每次返回一个值
内存消耗
可能较大
非常省内存
可暂停执行
不可以
可以暂停并恢复执行
3. yield 是啥?怎么用?
产出, 是生成器函数之中, 最为关键的点。它能够使得函数处于暂停的状态, 将相应的值传递出去, 之后等待获取下一次的调用指令。
def count_up_to(max): count = 1 while count <= max: yield count count += 1用法:
for num in count_up_to(3): print(num)输出:
1 2 3每次, 产出一个值, 下一轮, 从产出的下一行相邻处, 持续按照规则运行, 接着运行。
4. 用 for 遍历生成器函数
这属于最为常见的用法, 实际上其背后所蕴含的内容乃是迭代器相关原理, 也就是借助 next() 方法一点一点地去获取内容, 然而你并不需要去理会这些内容, 只要如同遍历列表那般去使用便可以了:
def gen_words(): yield "hello" yield "world" for word in gen_words(): print(word)输出:
hello world5. yield 与 的区别对比点
暂停函数
否(直接结束函数)
返回值
一次一个
一次性全部
多次调用
每次从上次 yield 后继续
每次都重新开始
例子对比:
def func_return(): return 1 return 2 # 永远不会执行 def func_yield(): yield 1 yield 26. 用 next() 手动调用生成器
可以不用 for,直接用 next() 来一个一个取值:
g = (x * 2 for x in range(3)) print(next(g)) # 0 print(next(g)) # 2 print(next(g)) # 4 # print(next(g)) # StopIteration对生成器函数也是一样:
def simple_gen(): yield "A" yield "B" g = simple_gen() print(next(g)) # A print(next(g)) # B7. yield from:更优雅地嵌套生成器
若在一个供产生的器具里调用另外一个供物产产生的器械, 能够以产生出息从这里开始替代数量众多的用于循环的器械。
传统写法:
def outer(): for val in [1, 2, 3]: yield val更简洁写法:
def outer(): yield from [1, 2, 3]也支持嵌套生成器函数:
def gen1(): yield "A" yield "B" def gen2(): yield from gen1() yield "C" for x in gen2(): print(x)输出:
A B C8. 生成器函数的应用场景
生成器函数在处理大量数据或流式数据时非常好用:
场景1:按行读取大文件
def read_large_file(path): with open(path, "r") as f: for line in f: yield line.strip()场景2:无限序列(如斐波那契)
def fib(): a, b = 0, 1 while True: yield a a, b = b, a + bf = fib() for _ in range(10): print(next(f))场景3:模拟数据流或日志流
import time def log_stream(): count = 0 while True: yield f"log line {count}" count += 1 time.sleep(1)9. 注意事项与陷阱10. 小结和写法建议
关键词记牢:
什么时候用生成器函数?