Python Yield 关键字
上次修改时间:2025 年 2 月 25 日
Python 中的 yield
关键字将函数转换为生成器,生成器可以暂停和恢复执行。与结束函数的 return
不同,yield
增量式地传递值。这使得可以高效地处理大型数据集。本指南探讨了生成器的创建、迭代和高级技术。
生成器在 yield 之间保留其状态。调用生成器函数会返回一个迭代器,并且每个 next
调用都会从上一个 yield
处恢复执行。这种惰性求值可以最大限度地减少内存使用,使其成为迭代任务的理想选择。
基本生成器函数
此示例演示了一个生成几个值的基本生成器。
def number_generator(): yield 10 yield 20 yield 30 gen = number_generator() print(next(gen)) # Output: 10 print(next(gen)) # Output: 20 print(next(gen)) # Output: 30
生成器按需生成值。每个 next
调用都会从上一个 yield
停止的地方继续,传递下一个值。在最后一个 yield 之后,它会引发 StopIteration
。
生成斐波那契数列
此生成器生成高达给定限制的斐波那契数。
def fibonacci(limit): a, b = 0, 1 while a < limit: yield a a, b = b, a + b for num in fibonacci(50): print(num, end=' ') # Output: 0 1 1 2 3 5 8 13 21 34
值仅在请求时计算,避免了将完整序列存储在内存中的需要。这种效率适合大型或无界范围。
内存效率比较
这将比较列表和生成器之间的内存使用情况。
import sys def list_squares(n): return [x**2 for x in range(n)] def gen_squares(n): for x in range(n): yield x**2 print(sys.getsizeof(list_squares(500))) # ~2300 bytes print(sys.getsizeof(gen_squares(500))) # ~112 bytes
生成器最小的内存占用源于按需生成值,这与列表不同,后者会预先存储所有内容。
向生成器发送值
生成器可以通过 send
接受值以进行交互式使用。
def accumulator(): total = 0 while True: value = yield total += value yield total gen = accumulator() next(gen) # Prime the generator gen.send(4) print(next(gen)) # Output: 4 gen.send(6) print(next(gen)) # Output: 10
使用 send
,将值馈送到生成器,从而更新其状态。这种双向通信支持动态计算。
Yield 表达式
这使用 yield
作为 send
的表达式。
def counter(): count = 0 while True: increment = yield count count += increment gen = counter() next(gen) # Start it print(gen.send(2)) # Output: 2 print(gen.send(3)) # Output: 5
生成器通过 send
接收增量并产生运行总计,从而展示了交互式状态管理。
处理异常
生成器使用 throw
和 close
处理异常。
def safe_gen(): try: while True: yield "Active" except ValueError: yield "Recovered" gen = safe_gen() print(next(gen)) # Output: Active print(gen.throw(ValueError)) # Output: Recovered gen.close() # Stops the generator
throw
方法会触发生成器内部的异常,这些异常可以被捕获和处理。close
会干净地终止它。
使用 Yield 的无限序列
这会生成一个无限的偶数序列。
def even_numbers(): n = 0 while True: yield n n += 2 evens = even_numbers() for _ in range(5): print(next(evens), end=' ') # Output: 0 2 4 6 8
生成器会无限地生成偶数,并按需产生每个值。循环或限制器控制消耗的数量。
带有子生成器的 Yield From
yield from
将产生操作委托给另一个生成器。
def sub_gen(): yield 1 yield 2 yield 3 def main_gen(): yield from sub_gen() yield 4 gen = main_gen() print(list(gen)) # Output: [1, 2, 3, 4]
yield from
语法简化了对 sub_gen
的委托,直接产生其值,然后进行额外的 yield。
下一个脚本演示了使用 yield from
在生成器之间委派任务。子生成器 process_file_lines
从文件中读取行,并通过在文件丢失时产生错误消息来优雅地处理错误。
def process_file_lines(filename): """Subgenerator that yields lines from a specific file.""" try: with open(filename, 'r') as file: for line in file: yield line.strip() except FileNotFoundError: yield f"Error: File '{filename}' not found" def process_multiple_files(file_list): """Main generator that delegates to process_file_lines for each file.""" for filename in file_list: print(f"Processing {filename}:") yield from process_file_lines(filename) yield "---" # Separator between files def main(): files_to_process = ["users.txt", "orders.txt", "missing.txt"] file_processor = process_multiple_files(files_to_process) # Process and display results for result in file_processor: print(result) if __name__ == "__main__": main()
主生成器 process_multiple_files
处理文件列表,并通过 yield from
委托给 process_file_lines
进行行读取。它在文件之间添加分隔符以提高可读性,并保持逻辑模块化和清晰。
在 main
中,程序使用示例文件列表来展示成功的处理和错误处理。这种方法突出了 yield from
如何简化代码并增强可维护性,尤其是在涉及嵌套任务的工作流程中。
使用 Yield 读取文件
这使用生成器逐行读取文件。
def read_file(filename): with open(filename, 'r') as f: yield from f for line in read_file('example.txt'): print(line.strip())
使用 yield from
,生成器会流式传输文件行,而无需将整个文件加载到内存中,从而提高了效率。
扁平化嵌套列表
这使用 yield from
递归地扁平化嵌套列表。
def flatten(items): for item in items: if isinstance(item, list): yield from flatten(item) else: yield item nested = [1, [2, 3], [4, [5]]] print(list(flatten(nested))) # Output: [1, 2, 3, 4, 5]
yield from
的递归使用会解包嵌套列表,并在平面序列中单独产生每个元素。
随机数据生成器
这会无限地生成随机整数。
import random def random_nums(min_val, max_val): while True: yield random.randint(min_val, max_val) rands = random_nums(1, 10) for _ in range(3): print(next(rands)) # e.g., Output: 7 3 9
生成器会无限地产生随机数,这对于测试或模拟很有用,并且仅在需要时才生成值。
使用 Yield 的最佳实践
- 用于大型数据: 处理大型数据集时,首选生成器以减少内存占用。
- 尽可能避免状态: 保持生成器无状态以简化操作,除非必要。
- 使用生成器表达式: 对于简单的情况,请使用
(x for x in iterable)
语法。 - 正确关闭: 完成后调用生成器的
close
以释放资源。
来源
本指南介绍了 yield
关键字在创建高效生成器中的作用,包括 yield from
等高级功能,用于委托和迭代。
作者
列出所有 Python 教程。