pin_drop当前位置:知识文库 ❯ 图文
Python yield from完全指南 - 委托生成器与嵌套生成器
一、什么是yield from
yield from是Python 3.3引入的新语法,用于简化生成器之间的委托调用。它允许一个生成器将部分或全部操作委托给另一个生成器、迭代器或可迭代对象。
在没有yield from之前,嵌套生成器需要显式编写循环来转发值,代码冗长且容易出错。yield from让这一切变得优雅简洁。
二、基础语法与工作原理
yield from iterable本质上等价于for item in iterable: yield item,但功能远不止于此。
代码示例
# 基础用法:等价替换
def old_way(iterable):
"""不使用yield from的方式"""
for item in iterable:
yield item
def new_way(iterable):
"""使用yield from的方式"""
yield from iterable
# 两种写法完全等价
print(list(old_way([1, 2, 3]))) # [1, 2, 3]
print(list(new_way([1, 2, 3]))) # [1, 2, 3]三、委托生成器与子生成器
在yield from的语境中,涉及两个关键角色:
-
委托生成器(Delegating Generator):包含yield from语句的生成器,负责将调用委托给子生成器
-
子生成器(Subgenerator):被yield from调用的生成器或可迭代对象,实际产生值的生成器
代码示例
# 子生成器
def sub_generator():
yield "子生成器: A"
yield "子生成器: B"
return "子生成器返回值"
# 委托生成器
def delegating_generator():
yield "委托: 开始"
result = yield from sub_generator() # 委托给子生成器
yield f"委托: 子生成器返回了 {result}"
yield "委托: 结束"
# 使用
gen = delegating_generator()
for item in gen:
print(item)
# 输出:
# 委托: 开始
# 子生成器: A
# 子生成器: B
# 委托: 子生成器返回了 子生成器返回值
# 委托: 结束四、代码示例与实战
示例1:展平嵌套列表
代码示例
def flatten(nested):
"""递归展平任意深度的嵌套列表"""
for item in nested:
if isinstance(item, (list, tuple)):
yield from flatten(item) # 递归委托
else:
yield item
# 测试
data = [1, [2, [3, 4], 5], [6, 7]]
print(list(flatten(data)))
# [1, 2, 3, 4, 5, 6, 7]
data2 = [["a", "b"], ["c", ["d", "e"]], "f"]
print(list(flatten(data2)))
# ['a', 'b', 'c', 'd', 'e', 'f']示例2:组合多个生成器
代码示例
def even_numbers():
for n in range(0, 10, 2):
yield n
def odd_numbers():
for n in range(1, 10, 2):
yield n
def combined():
yield "偶数:"
yield from even_numbers()
yield "奇数:"
yield from odd_numbers()
print(list(combined()))
# ['偶数:', 0, 2, 4, 6, 8, '奇数:', 1, 3, 5, 7, 9]示例3:链式管道处理
代码示例
def read_data():
"""模拟读取数据"""
yield from [1, 2, 3, 4, 5]
def square(data):
"""对每个元素求平方"""
for item in data:
yield item ** 2
def filter_positive(data):
"""过滤负数"""
for item in data:
if item > 0:
yield item
def process_pipeline():
"""组合管道:读取 -> 平方 -> 过滤"""
yield from filter_positive(square(read_data()))
print(list(process_pipeline()))
# [1, 4, 9, 16, 25]五、注意事项与最佳实践
注意1:yield from后面可以跟任何可迭代对象,不仅仅是生成器。列表、元组、字符串、文件对象等都可以作为yield from的参数。
注意2:子生成器的return语句返回值会被yield from表达式返回。这个特性在协程编程中非常重要,可以用来获取子任务的计算结果。
注意3:yield from会自动处理子生成器抛出的StopIteration异常,并在子生成器结束后恢复委托生成器的执行。不需要手动捕获和处理StopIteration。
小贴士
yield from是Python协程体系的重要基石。在asyncio中,await关键字的设计灵感就来源于yield from。理解yield from有助于深入理解Python异步编程的底层机制。
六、练习题
练习1
使用yield from实现一个生成器函数chain(*iterables),接受任意多个可迭代对象作为参数,依次产出每个可迭代对象的元素。功能类似itertools.chain。
练习2
使用yield from实现一个树形结构的遍历生成器。树的节点用{"value": 1, "children": [...]}的字典表示。使用yield from递归遍历所有子节点,返回所有节点的值。
常见问题
yield from和普通的for循环加yield有什么区别?
基础的值传递两者等价。但yield from还能处理send()、throw()和close()等方法的双向通信,并自动传递StopIteration异常,还能获取子生成器的return值。普通for+yield无法实现这些高级功能。
yield from可以用于非生成器的可迭代对象吗?
可以。yield from后面可以跟任何可迭代对象,包括列表、元组、字符串、range对象、文件对象等。它会隐式地将可迭代对象转换为迭代器并逐个产出元素。
yield from和async/await有什么关系?
await的底层设计与yield from非常相似。在Python 3.5引入async/await之前,协程就是通过yield from实现的。await可以看作是针对awaitable对象的yield from,提供了更清晰的语义。
本文涉及AI创作
内容由AI创作,请仔细甄别