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 教程。