Python 生成器(Generator)

什么是 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))
Python 生成器(Generator)

调用生成器函数会返回一个生成器对象,并且可以使用 next() 显式调用。

当一个生成器函数被调用时,它不会立即执行函数体,而是返回一个生成器对象。当使用这个生成器对象调用 next() 时,执行过程从上次返回(或开始)的位置继续,直到遇到下一个 yield 语句。

每次遇到 yield,生成器函数会暂停执行,并返回该 yield 后面的值。在下一次调用时,从暂停的地方继续执行。

与 return 的区别

  • yield 允许函数记住其执行状态,因此可以在多个 next() 调用之间保留状态。
  • return 会结束函数的执行并返回值,函数再也无法继续执行。

生成器的优势

使用 yield 生成器具有以下几个主要优势:

  1. 内存高效:生成器一次只生成一个项,适合处理大型数据集。
  2. 懒加载:仅在需要时生成数据,这适合具有复杂计算的场景,能够提高性能。
  3. 更加简洁的代码:使用 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

Like (0)
LJH的头像LJH
Previous 15小时前
Next 9小时前

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注