生成器是一种特殊的迭代器,通过yield实现惰性求值,调用生成器函数返回生成器对象,每次迭代时暂停并返回值,节省内存。适用于处理大文件、无限序列等场景,避免一次性加载所有数据。创建方式有生成器函数和生成器表达式,前者用yield返回值,后者类似列表推导式但使用圆括号,更节省内存。yield from可委托其他生成器,简化嵌套逻辑。

Python中的生成器(Generator)本质上是一种特殊的迭代器,它允许你以一种“惰性”的方式生成序列中的值,而不是一次性将所有值都存储在内存中。简单来说,它就像一个“按需供货”的工厂,只有当你真正需要下一个产品时,它才会生产出来,极大地节省了资源。
理解Python生成器,核心在于它的工作机制和它所解决的问题。它通过
yield
for
next()
yield
yield
yield
要创建一个生成器,最常见的方式有两种:
生成器函数(Generator Function): 这是最直接的方式,在函数定义中使用
yield
yield
def simple_generator():
print("开始生成...")
yield 1
print("生成了1")
yield 2
print("生成了2")
yield 3
print("生成了3")
# 调用生成器函数会返回一个生成器对象
gen = simple_generator()
# 迭代生成器
print(next(gen)) # 输出: 开始生成... \n 1
print(next(gen)) # 输出: 生成了1 \n 2
print(next(gen)) # 输出: 生成了2 \n 3
# print(next(gen)) # 会抛出 StopIteration 异常
# 也可以用for循环
print("\n使用for循环:")
for value in simple_generator():
print(value)
# 输出:
# 开始生成...
# 1
# 生成了1
# 2
# 生成了2
# 3
# 生成了3生成器表达式(Generator Expression): 这是一种更简洁的创建生成器的方式,语法上类似于列表推导式,但使用圆括号
()
[]
# 列表推导式 (一次性创建所有元素并存储)
my_list = [i * 2 for i in range(5)] # [0, 2, 4, 6, 8]
print(f"列表占用内存: {my_list.__sizeof__()} bytes")
# 生成器表达式 (按需生成,不占用额外内存存储所有元素)
my_generator = (i * 2 for i in range(5))
print(f"生成器对象占用内存: {my_generator.__sizeof__()} bytes") # 明显小于列表
print("\n迭代生成器表达式:")
for value in my_generator:
print(value) # 0, 2, 4, 6, 8 (逐个打印)说实话,我刚接触生成器的时候,觉得它有点“玄乎”,不就是能迭代吗?列表不也能迭代?但随着项目经验的增长,我逐渐意识到生成器在特定场景下简直是救星。它最核心的价值在于内存效率和处理无限序列的能力。
立即学习“Python免费学习笔记(深入)”;
想象一下,你正在处理一个巨大的日志文件,可能有几十GB甚至上百GB,或者需要从数据库中查询数百万条记录。如果试图一次性将所有数据都加载到内存中,你的程序很可能直接崩溃,或者变得异常缓慢。这时候,生成器就派上用场了。它允许你逐行(或逐条)读取和处理数据,每次只在内存中保留当前正在处理的那一小部分,而不是整个文件或所有记录。这对于资源受限的环境(比如嵌入式系统)或者需要处理“流式”数据(如网络数据包、实时传感器数据)的场景尤其重要。
再比如,你需要生成一个斐波那契数列,但你不知道到底需要多少个,或者可能需要一个“无限”长的数列。列表是无法存储无限序列的,但生成器可以。你可以编写一个生成器函数,它会按需不断地生成下一个斐波那契数,而不会耗尽内存。
def fibonacci_generator():
a, b = 0, 1
while True: # 理论上可以无限生成
yield a
a, b = b, a + b
# 只需要前10个斐波那契数
fib_gen = fibonacci_generator()
for _ in range(10):
print(next(fib_gen), end=" ") # 0 1 1 2 3 5 8 13 21 34
print()
# 如果需要更多,只需继续迭代,而不会预先计算所有值此外,生成器还能让代码变得更简洁、更符合“迭代器模式”的设计理念。当你的操作天然就是逐个处理元素时,用生成器来封装这个过程,代码会显得更清晰、更易于维护。它将“如何生成数据”和“如何使用数据”的逻辑优雅地分离开来。
yield
这大概是初学者最容易感到困惑的地方。我记得我当时就一直在想,
yield
return
最主要的区别在于:
返回类型和行为:
return
return
yield
next()
for
yield
yield
next()
yield
状态管理:
yield
next(gen_obj)
yield
yield expression
expression
next()
next(gen_obj)
yield
return
next()
StopIteration
def my_counter(n):
print("计数器启动")
i = 0
while i < n:
print(f"即将生成 {i}")
yield i
i += 1
print(f"生成 {i-1} 后,i变为 {i}")
print("计数器结束")
counter = my_counter(3)
print("第一次next()")
print(next(counter)) # 输出: 计数器启动 \n 即将生成 0 \n 0
print("第二次next()")
print(next(counter)) # 输出: 生成 0 后,i变为 1 \n 即将生成 1 \n 1
print("第三次next()")
print(next(counter)) # 输出: 生成 1 后,i变为 2 \n 即将生成 2 \n 2
print("第四次next() (会报错)")
try:
print(next(counter)) # 输出: 生成 2 后,i变为 3 \n 计数器结束 \n StopIteration
except StopIteration:
print("迭代结束了")通过这个例子,你可以清楚地看到
yield
除了生成器函数,Python还提供了另一种非常便捷的方式来创建生成器:生成器表达式。此外,对于更复杂的生成器组合场景,
yield from
生成器表达式 (Generator Expressions)
(expression for item in iterable if condition)
()
[]
sum((x*x for x in range(10)))
# 过滤偶数的平方
even_squares_gen = (x * x for x in range(10) if x % 2 == 0)
print("生成器表达式结果:")
for val in even_squares_gen:
print(val) # 0, 4, 16, 36, 64
# 直接在函数中使用,无需创建中间列表
total_sum = sum(x for x in range(1, 1000001)) # 计算1到100万的和,不会创建百万元素的列表
print(f"1到100万的和: {total_sum}")yield from
yield from iterable
yield from
for item in sub_generator: yield item
send()
throw()
close()
def sub_generator(start, end):
for i in range(start, end):
yield i
def main_generator():
print("开始生成第一个序列")
yield from sub_generator(1, 3) # 委托给sub_generator(1,3)
print("开始生成第二个序列")
yield from sub_generator(10, 12) # 委托给sub_generator(10,12)
print("生成完毕")
for val in main_generator():
print(val)
# 输出:
# 开始生成第一个序列
# 1
# 2
# 开始生成第二个序列
# 10
# 11
# 生成完毕yield from
yield
总的来说,生成器函数提供了最大的灵活性,适合复杂逻辑;生成器表达式则简洁高效,适合简单的转换;而
yield from
以上就是python中什么是生成器_Python生成器(Generator)概念与用法的详细内容,更多请关注php中文网其它相关文章!
python怎么学习?python怎么入门?python在哪学?python怎么学才快?不用担心,这里为大家提供了python速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号