生成器的代码执行权转移主要依赖于 Python 的 yield
关键字和生成器对象的特性。以下是详细的原理解释:
生成器的工作机制
-
生成器函数定义:
- 生成器函数是包含
yield
关键字的函数。 - 调用生成器函数不会执行函数体,而是返回一个生成器对象。
- 生成器函数是包含
-
生成器对象:
- 生成器对象是一个迭代器,实现了
__iter__()
和__next__()
方法。
- 生成器对象是一个迭代器,实现了
-
代码执行权转移:
-
初始调用: 当生成器对象的
__next__()
方法被调用时,生成器函数开始执行,直到遇到第一个yield
。 -
暂停执行: 在
yield
处,函数暂停执行,将值返回给调用者,并保存当前的执行状态(局部变量、指令指针等)。 -
恢复执行: 再次调用
__next__()
时,生成器从上次暂停的地方继续执行,直到遇到下一个yield
或函数结束。
-
初始调用: 当生成器对象的
-
结束执行:
- 当生成器函数执行完所有代码或显式返回时,会抛出
StopIteration
异常,通知调用者迭代结束。
- 当生成器函数执行完所有代码或显式返回时,会抛出
具体执行流程
- 启动: 生成器函数被调用,返回生成器对象。
-
迭代:
- 调用
__next__()
,执行函数体,遇到yield
,返回值并暂停。 - 控制权转移到调用者,调用者可以处理返回的值。
- 调用者再次调用
__next__()
,生成器继续执行。
- 调用
-
终止: 函数执行完毕或显式返回,抛出
StopIteration
。
关键点
- 状态保存: 生成器在暂停时保存其执行状态,包括局部变量和执行位置。
- 惰性求值: 生成器仅在需要时计算值,节省内存。
-
控制权转移:
yield
实现了生成器和调用者之间的控制权转移。
这种机制使生成器在处理流式数据和协作式任务时非常高效。
以下结合例子来解读生成器代码执行权转移原理
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from contextlib import asynccontextmanager
from sqlalchemy import Table, Column, Integer, Float, String, DateTime, Index
from config.settings import config
DATABASE_URL = f'postgresql+asyncpg://{config.PG_USER}:{config.PG_PASSWORD}@{config.PG_HOST}:{config.PG_PORT}/{config.PG_DATABASE}'
Base = declarative_base()
class Database:
def __init__(self, database_url=DATABASE_URL):
self.engine = create_async_engine(
url=database_url,
pool_recycle=300,
pool_size=20,
max_overflow=10,
pool_timeout=30,
# echo=config.SQL_PRINT
)
self.async_session = sessionmaker(bind=self.engine,
class_=AsyncSession,
expire_on_commit=False)
@asynccontextmanager
async def session_scope(self):
session = self.async_session()
try:
yield session
await session.commit()
except Exception:
await session.rollback()
raise
finally:
await session.close()
# 其他方法保持不变,或根据需要进行异步化处理
# 使用示例
async def create_tables():
async with database.session_scope() as session:
# 在这里执行数据库操作
pass
在这个代码中,生成器的执行转移原理体现在 asynccontextmanager
装饰器的使用上。asynccontextmanager
是 Python 的标准库 contextlib
提供的一个工具,用于创建异步上下文管理器。它利用生成器的特性来管理资源的获取和释放。
代码解读
-
asynccontextmanager
装饰器:- 这个装饰器将一个生成器函数转换为异步上下文管理器。
- 生成器函数在
yield
之前的代码用于设置上下文(如资源获取),在yield
之后的代码用于清理上下文(如资源释放)。
-
session_scope
方法:-
async with database.session_scope() as session
: 进入上下文管理器时,session_scope
被调用。 -
执行过程:
-
开始上下文:
session = self.async_session()
创建一个新的数据库会话。 -
yield
暂停:yield session
暂停执行,将session
对象返回给调用者,供其使用。 -
恢复执行: 当
async with
块退出时,控制权返回到session_scope
方法,继续执行yield
之后的代码。
-
开始上下文:
-
-
异常处理和资源清理:
-
异常处理: 如果在
async with
块中抛出异常,except
块会捕获异常,调用await session.rollback()
。 -
资源释放: 在
finally
块中,无论是否有异常,都会执行await session.close()
,确保会话被正确关闭。
-
异常处理: 如果在
生成器中执行权的转移
-
初次执行:
async with
语句调用session_scope
,执行到yield session
,将控制权转移给调用者。 -
暂停与恢复: 在
yield
处暂停,调用者使用session
执行数据库操作。 -
结束执行:
async with
块结束后,控制权返回生成器,继续执行yield
之后的代码,进行异常处理和资源清理。
优势
-
简洁性: 使用
asynccontextmanager
可以简洁地管理异步资源。 - 自动化: 自动处理资源的获取和释放,减少手动管理的复杂性。
-
异步支持: 适用于异步编程模型,支持
await
语句。
这种机制使得异步上下文管理器在处理数据库连接等资源时既高效又安全。