欢迎光临
我们一直在努力

Python 中的生成器(Generators in Python)

Python 中的生成器(Generators in Python)

flyfish

生成器(Generator)是 Python 中一种特殊的迭代器,它允许函数在执行过程中暂停并返回一个值,然后在下次调用时从暂停的地方继续执行。这使得生成器特别适合处理大规模数据或需要惰性计算的场景,而不会一次性加载所有数据到内存中。生成器是通过在函数中使用 yield 关键字来定义的,而不是普通的 return。当一个函数包含 yield 时,它就变成了生成器函数,调用它不会立即执行函数体,而是返回一个生成器对象。可以通过 next() 函数或 for 循环来逐步获取生成的值。

yield 的主要作用是将一个普通函数转换为一个生成器(Generator)。
当一个函数中包含 yield 语句时,它就不再是一个普通函数了。调用这个函数不会立即执行函数体,而是会返回一个生成器对象。

def simple_generator():
"""一个简单的生成器函数,按顺序生成三个值"""
print("即将生成第一个值…")
yield 1 # 暂停,返回 1
print("即将生成第二个值…")
yield 2 # 恢复执行,暂停,返回 2
print("即将生成第三个值…")
yield 3 # 恢复执行,暂停,返回 3
print("生成器函数执行完毕。")

# 1. 创建生成器对象
# 注意:此时函数体并未执行
gen = simple_generator()
print("生成器对象已创建,但函数还未开始执行。")

# 2. 使用 next() 函数来获取下一个值
print("\\n— 第一次调用 next() —")
value1 = next(gen)
print(f"获取到的值: {value1}")

print("\\n— 第二次调用 next() —")
value2 = next(gen)
print(f"获取到的值: {value2}")

print("\\n— 第三次调用 next() —")
value3 = next(gen)
print(f"获取到的值: {value3}")

print("\\n— 第四次调用 next() —")
try:
# 当没有更多 yield 语句时,再次调用 next() 会抛出 StopIteration 异常
value4 = next(gen)
except StopIteration:
print("捕获到 StopIteration 异常,生成器已耗尽。")

# 3. 使用 for 循环遍历生成器(更常见的用法)
# for 循环会自动处理 StopIteration 异常,所以代码更简洁
print("\\n— 使用 for 循环遍历 —")
for value in simple_generator():
print(f"循环中获取到的值: {value}")

运行结果:

生成器对象已创建,但函数还未开始执行。

—– 第一次调用 next() —–
即将生成第一个值...
获取到的值: 1

—– 第二次调用 next() —–
即将生成第二个值...
获取到的值: 2

—– 第三次调用 next() —–
即将生成第三个值...
获取到的值: 3

—– 第四次调用 next() —–
生成器函数执行完毕。
捕获到 StopIteration 异常,生成器已耗尽。

—– 使用 for 循环遍历 —–
即将生成第一个值...
循环中获取到的值: 1
即将生成第二个值...
循环中获取到的值: 2
即将生成第三个值...
循环中获取到的值: 3
生成器函数执行完毕。

例子2:生成无限序列

生成器的一个巨大优势是可以轻松地表示无限序列,因为它不需要在内存中存储所有元素。

def infinite_sequence():
"""生成一个无限的整数序列:1, 2, 3, 4, …"""
num = 1
while True:
yield num
num += 1

# 创建无限序列的生成器
inf_gen = infinite_sequence()

# 我们可以从中取出任意数量的值,而不会耗尽内存
print("从无限序列中取出前5个值:")
for _ in range(5):
print(next(inf_gen))

print("\\n再取出接下来的3个值:")
for _ in range(3):
print(next(inf_gen))

运行结果:

从无限序列中取出前5个值:
1
2
3
4
5

再取出接下来的3个值:
6
7
8

例子3:生成器表达式(Generator Expression)

创建生成器的一种更简洁的语法,类似于列表推导式(List Comprehension),但使用圆括号 () 而不是方括号 []。

# 列表推导式:一次性在内存中创建一个包含100万个元素的列表
# 这会消耗大量内存!
print("创建列表推导式…")
squared_list = [x * x for x in range(1000000)]
print(f"列表推导式创建完成,类型: {type(squared_list)}")
# print(f"列表内容: {squared_list[:5]}…") # 打印前5个元素
print(f"列表占用的内存 (大约): {squared_list.__sizeof__() / 1024 / 1024:.2f} MB\\n")

# 生成器表达式:只创建一个生成器对象,不立即计算任何值
print("创建生成器表达式…")
squared_gen = (x * x for x in range(1000000))
print(f"生成器表达式创建完成,类型: {type(squared_gen)}")
print(f"生成器占用的内存 (非常小): {squared_gen.__sizeof__()} bytes\\n")

# 使用生成器表达式
print("遍历生成器表达式的前5个值:")
for i, value in enumerate(squared_gen):
print(value)
if i == 4:
break

运行结果:

创建列表推导式...
列表推导式创建完成,类型: <class 'list'>
列表占用的内存 (大约): 8.06 MB

创建生成器表达式...
生成器表达式创建完成,类型: <class 'generator'>
生成器占用的内存 (非常小): 184 bytes

遍历生成器表达式的前5个值:
0
1
4
9
16

例子4:作为布尔表达式进行计数

def count_word_occurrences(text, target_word):
"""统计目标单词在文本中出现的次数"""
words = text.split()
for word in words:
# 如果单词匹配,就 yield True (相当于 1)
yield word == target_word

text = "hello world, hello python, hello generator"
target = "hello"

# sum() 会遍历生成器,将所有 True (1) 相加
occurrences = sum(count_word_occurrences(text, target))
print(f"单词 '{target}' 出现了 {occurrences} 次。") # 输出: 单词 'hello' 出现了 3 次。

yield 和 return 的区别

特性 (Feature)yieldreturn
核心用途 用于将一个普通的 Python 函数转换为生成器。 用于结束函数的执行,并将结果返回给调用者。
执行流程 暂停函数的执行,但不销毁局部变量。 退出函数,终止其执行。
返回时机 当生成器需要向调用者提供一个中间结果时使用。 当函数计算完成,准备向调用者发送最终结果时使用。
后续代码 yield 语句之后的代码会在下次调用生成器时执行。 return 语句之后的代码永远不会被执行。
执行次数 一个生成器函数可以被多次恢复执行(即多次 yield)。 一个函数在单次调用中,return 只会被执行一次,然后函数结束。
执行起点 每次恢复执行时,从上次暂停的状态(即上次 yield 之后)继续。 每次函数调用都从头开始执行。

yield 局限性

  • 不支持随机访问:
    与列表(list)不同,不能通过索引(如 my_generator[5])直接访问生成器中的任意元素。必须从头开始迭代,直到到达你想要的位置。

  • 一次性使用:
    生成器是单向迭代器。一旦遍历完所有元素(即生成器被“耗尽”),它就不能被重新使用了。如果需要再次遍历,必须重新创建一个新的生成器对象。

  • 赞(0)
    未经允许不得转载:171主机测评 » Python 中的生成器(Generators in Python)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址