什么是 Python 生成器
Python 生成器(Generator) 是理解 Python 异步、惰性计算、协程机制等高级特性的基石。很多框架(如 asyncio、爬虫、流式处理)底层都依赖生成器。
生成器表达式构造生成器
生成器表达式类似于列表推导式(格式:new_list = [expression for item in iterable if condition]
),但返回的是一个生成器对象,而不是实际的列表,用于惰性生成值(按需一个个地返回结果,而不是一次性全部计算)。
生成器表达式的语法:
new_generator = (expression for item in iterable if condition)
列表推导式:
# 使用列表推导式生成一个包含平方的列表
squares = [x**2 for x in range(5)]
print(squares) # 输出: [0, 1, 4, 9, 16]
生成器表达式:
# 使用生成器表达式生成一个包含平方的生成器
squares_gen = (x**2 for x in range(5))
for square in squares_gen:
print(square) # 输出: 0 1 4 9 16(每次输出一个,直到结束)
yield
构造生成器函数
yield
是用于定义生成器函数的关键字。当一个函数包含 yield
语句时,它不再是普通的函数,而是一个生成器函数。调用这个生成器函数会返回一个生成器对象。
使用 yield
定义生成器函数:
def my_generator():
yield 1 # 第一次调用 next() 时返回 1
yield 2 # 第二次调用 next() 时返回 2
yield 3 # 第三次调用 next() 时返回 3
new_generator=my_generator()
print(type(new_generator))

调用生成器函数会返回一个生成器对象,并且可以使用 next()
显式调用。
当一个生成器函数被调用时,它不会立即执行函数体,而是返回一个生成器对象。当使用这个生成器对象调用 next() 时,执行过程从上次返回(或开始)的位置继续,直到遇到下一个 yield 语句。
每次遇到 yield
,生成器函数会暂停执行,并返回该 yield
后面的值。在下一次调用时,从暂停的地方继续执行。
与 return
的区别
yield
允许函数记住其执行状态,因此可以在多个next()
调用之间保留状态。return
会结束函数的执行并返回值,函数再也无法继续执行。
生成器的优势
使用 yield
生成器具有以下几个主要优势:
- 内存高效:生成器一次只生成一个项,适合处理大型数据集。
- 懒加载:仅在需要时生成数据,这适合具有复杂计算的场景,能够提高性能。
- 更加简洁的代码:使用
yield
使代码灵活,创建复杂的迭代行为变得简单。
普通函数 vs 生成器函数
普通函数:一次性返回结果
def square_list(n):
return [i*i for i in range(n)]
print(square_list(5)) # [0, 1, 4, 9, 16]
生成器函数:每次返回一个值,用 yield
def gen_squares(n):
for i in range(n):
yield i*i # 返回一个值,函数暂停执行
gen = gen_squares(5)
print(next(gen)) # 0
print(next(gen)) # 1
yield 与 next 的执行机制
def count():
print("start")
yield 1
print("middle")
yield 2
print("end")
yield 3
g = count()
print(next(g)) # 执行到第一个 yield
print(next(g)) # 从上次暂停的地方继续执行
每次 yield
暂停函数执行,next()
再次唤醒
使用生成器生成斐波那契数列练习
生成斐波那契数列的生成器是一个非常好的练习,能够帮助您理解生成器函数是如何工作的。斐波那契数列是一个简单而经典的数列,每个数字都是前两个数字的和,通常以 0 和 1 开头。数列的前几个数字是:0, 1, 1, 2, 3, 5, 8, 13, ...
def fibonacci(n):
a, b = 0, 1 # 初始化斐波那契数列的前两个数
for _ in range(n): # 生成 n 个斐波那契数
yield a # 将当前值返回,并暂停函数
a, b = b, a + b # 更新 a 和 b 的值
# 使用生成器生成前 10 个斐波那契数
fib_gen = fibonacci(10)
for number in fib_gen:
print(number)
生成器对象特性
- 不占内存(不像列表)
- 可以无限大(如生成无限斐波那契数列)
- 不能反复遍历(一旦耗尽,就没了)
生成器的通信机制
send()
, throw()
, close()
生成器的通信机制主要体现在其能够通过 send()
、throw()
和 close()
方法与外部代码进行交互。
send()
send(value)
方法可以用于恢复生成器的执行,并将一个值传递给生成器的 yield
表达式。当生成器执行到 yield
语句时,它会暂停并返回当前的输出值。此时,您可以使用 send()
继续执行生成器,从而改变其运行状态。
示例:
def generator():
value = yield "Initial value" # 第一次到 yield 暂停,返回 "Initial value"
yield f"Received: {value}"
gen = generator()
print(next(gen)) # 输出: Initial value
print(gen.send("Hello")) # 输出: Received: Hello
在这个例子中,第一次调用 next(gen)
会返回 "Initial value"
。随后,调用 gen.send("Hello")
会将 "Hello"
传递给生成器的 value
,并继续执行,直到遇到下一个 yield
。
throw()
throw(type, value=None, traceback=None)
方法可以用来在生成器内部抛出一个异常。它从暂停的位置恢复执行,并在此位置引发指定的异常。
示例:
def generator():
try:
yield 1
except ValueError as e:
yield f"Caught an exception: {str(e)}"
gen = generator()
print(next(gen)) # 输出: 1
print(gen.throw(ValueError, "This is an error!")) # 输出: Caught an exception: This is an error!
在这个示例中,gen.throw(ValueError, "This is an error!")
会在生成器内部引发 ValueError
异常,并返回处理后的字符串。
close()
close()
方法用于关闭生成器,使其不再可用。如果在生成器执行过程中调用 close()
,它会引发 StopIteration
异常。
示例:
def generator():
yield 1
yield 2
gen = generator()
print(next(gen)) # 输出: 1
gen.close() # 关闭生成器
# print(next(gen)) # 将引发 StopIteration 异常
生成器与协程
yield from
, async def
+ await
与生成器的演进
生成器的功能并不仅限于简单的迭代。由于其支持值的发送与接收,使得生成器也可以作为轻量级的协程。在 Python 3.5 及以后的版本中,引入了新的异步功能,进一步增强了协程的能力。
yield from
yield from
是 Python 3.3 中引入的,提供了一种结构化的方式来委托生成器,并将控制权传递给另一个生成器。这使得在生成器内部可以轻松调用其他生成器。- 示例:
def subgenerator():
yield from range(3) # 委托给另一个生成器
def main_generator():
yield from subgenerator()
for value in main_generator():
print(value) # 输出: 0 1 2
在这个例子中,main_generator
委托给了 subgenerator
,从而直接使用了 subgenerator
生成的值。
async def 和 await
- Python 3.5 之前的生成器需要使用
yield
和yield from
来处理异步操作。Python 3.5 引入了async def
和await
关键字使得编写异步代码更加清晰和直观。 async def
用于定义异步函数,内部使用await
关键字来暂停执行直到某个可等待对象(如协程)完成。
import asyncio
async def async_function():
print("Start async function")
await asyncio.sleep(1) # 模拟异步操作
print("End async function")
async def main():
await async_function() # 等待 async_function 执行完成
# 运行主协程
asyncio.run(main())
在这个例子中,async_function
是一个异步函数,使用 await
等待 asyncio.sleep(1)
完成,而不是通过 yield
来手动控制。
发布者:LJH,转发请注明出处:https://www.ljh.cool/43112.html