生成器函数与普通函数的本质区别在于:普通函数执行后返回值并销毁状态,而生成器函数通过yield暂停并保持状态,返回生成器对象实现惰性求值和内存高效迭代。

Python中的生成器函数和
yield
yield
当我第一次接触
yield
return
yield
yield
每次我们从生成器对象中请求下一个值(比如通过
for
next()
yield
return
return
StopIteration
这种“惰性求值”的机制,是我认为
yield
立即学习“Python免费学习笔记(深入)”;
# 一个简单的生成器函数示例
def my_range(n):
i = 0
while i < n:
yield i
i += 1
# 使用生成器
print("通过for循环迭代:")
for num in my_range(5):
print(num)
print("\n手动通过next()获取:")
gen = my_range(3)
print(next(gen)) # 输出 0
print(next(gen)) # 输出 1
print(next(gen)) # 输出 2
try:
print(next(gen)) # 尝试获取第四个值,会触发StopIteration
except StopIteration:
print("迭代结束了!")你看,它就像一个聪明的工厂,不是一次性生产所有产品,而是接到订单才生产一个,然后等着下一个订单。
我觉得理解生成器和普通函数的核心差异,是掌握
yield
return
return
None
而生成器函数,它遇到
yield
yield
从技术层面讲,普通函数返回的是一个具体的值,而生成器函数返回的是一个迭代器(或者说生成器对象),这个对象实现了迭代器协议(即有
__iter__
__next__
for
next()
我个人在使用Python处理数据时,一旦发现需要迭代处理大量数据,或者数据源是流式的、无限的,我几乎会本能地想到生成器。
处理大型文件或数据集: 这是最典型的应用场景。比如你需要读取一个日志文件,分析其中包含特定关键字的行。如果文件有几十GB,你不可能一次性读入内存。用生成器,你可以逐行读取,逐行处理,内存占用始终保持在一个非常低的水平。
def read_large_file(filepath):
# 实际应用中,这里应该处理文件不存在等异常
try:
with open(filepath, 'r', encoding='utf-8') as f:
for line in f:
yield line.strip()
except FileNotFoundError:
print(f"文件未找到: {filepath}")
# 可以选择抛出异常或返回空生成器
return
# 假设有一个很大的'access.log'文件
# 创建一个模拟的大文件
with open('test_access.log', 'w') as f:
for i in range(10000):
f.write(f"Line {i}: This is a log entry. {'ERROR' if i % 100 == 0 else ''}\n")
print("\n处理模拟日志文件:")
for log_entry in read_large_file('test_access.log'):
if "ERROR" in log_entry:
print(f"发现错误日志: {log_entry}")生成无限序列: 比如斐波那契数列,或者一个计数器。你无法一次性生成所有这些数字,因为它们是无限的。生成器可以按需提供。
def fibonacci_sequence():
a, b = 0, 1
while True: # 无限循环,因为斐波那契数列是无限的
yield a
a, b = b, a + b
print("\n生成斐波那契数列的前10个数字:")
fib_gen = fibonacci_sequence()
for _ in range(10):
print(next(fib_gen)) # 打印前10个斐波那契数数据管道和流式处理: 当你需要对数据进行一系列转换时,生成器可以构建一个高效的“管道”。每个生成器函数负责一个步骤,数据在管道中流动,每次只处理一小块,而不是在每个步骤都创建中间列表。
def filter_evens(numbers):
for num in numbers:
if num % 2 == 0:
yield num
def square_numbers(numbers):
for num in numbers:
yield num * num
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 传统方式可能需要创建中间列表,占用更多内存
# evens = [n for n in data if n % 2 == 0]
# squared_evens = [n*n for n in evens]
# 生成器管道
print("\n通过生成器管道处理数据:")
processed_data = square_numbers(filter_evens(data))
for res in processed_data:
print(res) # 输出 4, 16, 36, 64, 100这种链式调用,在我看来,不仅高效,代码也更清晰。
当我们谈到生成器时,除了生成器函数,还有一个非常简洁且同样强大的工具——生成器表达式。我常常把它看作是列表推导式(List Comprehension)的“惰性”版本。
生成器表达式的语法非常像列表推导式,只是它使用圆括号
()
[]
# 列表推导式:立即创建一个包含所有元素的列表
# my_list = [x * x for x in range(1000000)] # 会立即占用大量内存,慎用大范围
# 生成器表达式:创建一个生成器对象,按需生成值
my_gen_exp = (x * x for x in range(1000000)) # 几乎不占用内存,直到你迭代它
print("\n生成器表达式示例:")
print(f"生成器表达式对象: {my_gen_exp}")
print(f"第一个值: {next(my_gen_exp)}") # 输出 0
print(f"第二个值: {next(my_gen_exp)}") # 输出 1
# 我们可以继续迭代
for _ in range(3):
print(next(my_gen_exp)) # 输出 4, 9, 16区别:
def
yield
相同点:
for
next()
在我看来,如果你需要一个简单的、一次性的迭代器,生成器表达式是优雅且高效的选择。但如果逻辑更复杂,或者你需要封装更强的复用性,那么生成器函数无疑是更好的工具。选择哪一个,更多时候取决于你代码的复杂度和可读性需求。
以上就是Python中生成器函数用法详解 Python中yield关键字教程的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号