在 Python 中,yield 是一个关键字,用于定义生成器函数。生成器函数是一种特殊的函数,可以用来产生一个序列的值,而不是一次性返回所有值,这样可以节省内存并提高效率。
使用 yield 关键字定义的函数会在每次调用时生成一个值,并在下一次调用时恢复执行。生成器函数会记住上次执行的状态,以便从上次停止的地方继续执行。这种延迟生成值的方式使得生成器函数非常适合处理大量数据或无限序列。
下面是一个简单的示例,演示了如何使用 yield 定义生成器函数:
def my_generator():
yield 1
yield 2
yield 3
# 使用生成器函数
gen = my_generator()
# 逐个获取生成器的值
print(next(gen)) # 输出:1
print(next(gen)) # 输出:2
print(next(gen)) # 输出:3
# 超出范围将引发 StopIteration 异常
print(next(gen)) # 引发 StopIteration 异常
在这个示例中,my_generator() 是一个生成器函数,使用 yield 关键字产生了三个值。可以通过调用 next() 函数逐个获取生成器的值,直到生成器结束。如果尝试获取超出生成器范围的值,将引发 StopIteration 异常。
除了简单地生成连续的序列之外,yield 还可以用于更复杂的生成器函数,例如在生成器函数中使用循环、条件语句等。下面是一个稍微复杂一些的示例,演示了如何使用 yield 生成斐波那契数列:
def fibonacci(n):
a, b = 0, 1
count = 0
while count < n:
yield a
a, b = b, a + b
count += 1
# 使用生成器函数生成斐波那契数列
fib_gen = fibonacci(10)
# 遍历并输出斐波那契数列的前10个数
for num in fib_gen:
print(num)
在这个示例中,fibonacci() 是一个生成器函数,使用 yield 关键字生成了斐波那契数列的前 n 个数。在循环中,通过不断更新变量 a 和 b 来计算下一个斐波那契数,并使用 yield 关键字将其生成。然后,我们使用 for 循环遍历生成器,并输出斐波那契数列的前 10 个数。
这种方法可以节省内存,因为在生成器函数中只需要存储有限的状态,而不需要一次性生成所有斐波那契数。这在处理大量数据或者需要无限生成序列的情况下非常有用。
更复杂的示例可能涉及到生成器函数与其他 Python 功能的结合使用,例如异常处理、文件 I/O、迭代器等。下面是一个稍复杂的示例,演示了如何使用生成器函数来实现一个自定义的日志记录器,将日志信息写入文件并实现日志滚动功能:
import os
import datetime
def log_writer(log_file, max_size=1024, backup_count=5):
size = 0
count = 0
while True:
try:
# 检查文件大小是否超过限制
if os.path.exists(log_file) and os.path.getsize(log_file) > max_size:
# 备份日志文件
count += 1
backup_log = f"{log_file}.{count}"
os.rename(log_file, backup_log)
yield f"Backup log: {backup_log}"
# 写入日志
timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
log_msg = f"{timestamp} - Log message\n"
with open(log_file, 'a') as f:
f.write(log_msg)
size += len(log_msg)
yield log_msg
except Exception as e:
yield f"Error: {str(e)}"
# 检查备份文件数量是否超过限制
if count >= backup_count:
count = 0
# 使用生成器函数记录日志
log_gen = log_writer("example.log", max_size=1024, backup_count=3)
# 写入日志并输出日志信息
for msg in log_gen:
print(msg)
在这个示例中,log_writer() 是一个生成器函数,它会不断地生成日志信息并写入文件。如果日志文件大小超过 max_size 的限制,它会将当前日志文件备份,并创建一个新的日志文件继续写入。备份文件的数量受 backup_count 控制,超过限制时会从头开始覆盖备份文件。