在Python中,yield
关键字用于定义生成器(generator)函数。生成器是一种特殊的迭代器,它允许你按需产生一系列的值,而不是一次性生成所有值并存储在内存中。这使得处理大数据集合时更加高效和节省资源。yield
就是像一个park标识牌,遇到的时候,将当前值返回,当下一次被获取的时候,从标识牌位置往下继续运行,和循环的差别是调用一次计算一次return一次,不是全部算完。以下是yield
的一些基本使用方法及代码案例:
基本使用
案例1:简单生成器
def simple_generator():
yield 1
yield 2
yield 3
# 使用生成器
for value in simple_generator():
print(value)
无限序列
案例2:生成无限序列
def infinite_sequence():
num = 0
while True:
yield num
num += 1
# 打印前10个数
for i, val in enumerate(infinite_sequence()):
if i >= 10:
break
print(val)
计算斐波那契数列
案例3:斐波那契数列
斐波那契数列:后面的数=前两个数之和
def fibonacci(n):
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
# 打印前10个斐波那契数
for num in fibonacci(10):
print(num)
使用生成器表达式
案例4:生成器表达式
生成器表达式类似于列表推导式,但使用圆括号而非方括号,并返回一个生成器对象。
squared_gen = (x**2 for x in range(5))
for square in squared_gen:
print(square)
作为函数参数
案例5:生成器作为函数参数
生成器可以作为需要迭代对象的函数参数。
def sum_of_squares(nums):
return sum(x**2 for x in nums)
print(sum_of_squares(range(10)))
与迭代器协议结合
案例6:实现迭代器协议
通过在类中使用yield
,可以创建遵循迭代器协议的类实例。
class CountDown:
def __init__(self, start):
self.start = start
def __iter__(self):
current = self.start
while current > 0:
yield current
current -= 1
# 使用自定义生成器类
for num in CountDown(5):
print(num)
以上是yield
的一些基本用法。
下面是一些进阶用法和实际应用场合:
生成器委托(Generator Delegation)
从Python 3.3开始,生成器可以通过yield from
语法委托给另一个生成器(或者任何可迭代对象),这样可以更简洁地组合多个生成器的行为。
案例7:生成器委托
def first_generator():
for i in range(3):
yield i
def combined_generator():
yield from first_generator()
yield from range(3, 6)
# 打印组合后的序列
for num in combined_generator():
print(num)
生成器作为协程(Coroutines)
虽然严格意义上这不是yield
的直接用法,但在Python中,生成器常被用作简单的协程,实现非阻塞的异步编程模式。通过发送值到生成器并从生成器接收值,可以模拟复杂的控制流。
案例8:生成器作为协程
def coroutine_example():
while True:
x = yield
print(f"Received: {x}")
# 创建协程实例并启动
cr = coroutine_example()
next(cr) # 初始化协程,必须先调用next或send(None)
cr.send("Hello") # 发送数据到协程
cr.send("World") # 再次发送数据
使用生成器进行延迟计算
生成器可以用来实现惰性求值,即只在需要时计算下一个值。
案例9:延迟计算平方
def lazy_square(numbers):
for num in numbers:
yield num ** 2
numbers = [1, 2, 3, 4, 5]
squared_gen = lazy_square(numbers)
# 只有在迭代时才会计算平方
for square in squared_gen:
print(square)
这些示例展示了yield
在构建复杂逻辑、实现高效数据处理流程、以及在协程编程中的重要作用。生成器的强大之处在于它们能够灵活地控制程序的执行流程,同时保持高效的内存使用
。
给人的感觉和for循环的差别就是省下了内存,这个在一般情况下是用处不大的,但是在关键场景还是会发挥很大作用的,那你们有没有使用过呢?