
在python中,`__init__`方法不能直接使用`await`关键字执行异步操作。尝试在构造器中强制执行异步代码会导致性能问题或ide警告。本文将探讨为什么应避免在`__init__`中进行异步初始化,并介绍使用异步工厂方法作为最佳实践,以确保类能够被轻松构造,同时实现异步资源的正确初始化。
在Python的异步编程模型中,async def函数是协程,它们能够暂停执行并等待其他异步操作完成。然而,类的构造方法__init__是一个同步方法,Python语言规范不允许将其定义为async def __init__。这意味着任何尝试在__init__内部直接使用await关键字的代码都会导致语法错误。
例如,在一个需要与Cosmos数据库进行交互的FastAPI项目中,我们可能希望在创建数据库操作类实例时,就确保数据库和容器已经存在:
from azure.cosmos.aio import CosmosClient # 假设这是一个异步客户端
class CosmosCRUD:
def __init__(self, client: CosmosClient):
self.client = client
# 以下代码会导致语法错误,因为__init__不能await
# self.database = await self.client.create_database_if_not_exists("MY_DATABASE_NAME")
# self.container = await self.database.create_container_if_not_exists("MY_CONTAINER_NAME", partition_key=...)面对这一限制,开发者可能会考虑以下几种方案:
这些方案都存在各自的缺陷,通常不被推荐。
立即学习“Python免费学习笔记(深入)”;
一个核心的设计原则是:类应可轻易构造(trivially constructible)。这意味着__init__方法应该只负责初始化实例的基本状态,例如接收并存储必要的参数,声明实例属性,而不应执行耗时、复杂或具有副作用的操作。将异步操作放入__init__会带来以下问题:
处理类中异步初始化逻辑的最佳实践是使用异步工厂方法(Asynchronous Factory Method)。这种模式将对象的创建和异步初始化逻辑清晰地分离。
核心思想:
from azure.cosmos.aio import CosmosClient, DatabaseProxy, ContainerProxy
import asyncio
class CosmosCRUD:
"""
Cosmos DB CRUD操作类,采用异步工厂模式进行初始化。
"""
def __init__(self, client: CosmosClient, database_name: str, container_name: str, partition_key_path: str):
"""
同步构造器,仅用于声明和存储基本属性。
异步资源在此处声明为None,等待异步工厂方法进行初始化。
"""
self.client: CosmosClient = client
self.database_name: str = database_name
self.container_name: str = container_name
self.partition_key_path: str = partition_key_path
self.database: DatabaseProxy = None # 声明类型,但初始为None
self.container: ContainerProxy = None # 声明类型,但初始为None
print("CosmosCRUD: __init__ called (synchronous part)")
@classmethod
async def create(cls, client: CosmosClient, database_name: str, container_name: str, partition_key_path: str):
"""
异步工厂方法,负责创建实例并执行异步初始化逻辑。
"""
# 1. 调用同步__init__创建实例
instance = cls(client, database_name, container_name, partition_key_path)
# 2. 执行异步初始化
await instance._async_initialize_resources()
return instance
async def _async_initialize_resources(self):
"""
内部异步初始化方法,处理所有异步资源创建和设置。
"""
print(f"CosmosCRUD: _async_initialize_resources - Ensuring database '{self.database_name}' exists...")
self.database = await self.client.create_database_if_not_exists(self.database_name)
print(f"CosmosCRUD: _async_initialize_resources - Ensuring container '{self.container_name}' exists...")
self.container = await self.database.create_container_if_not_exists(
self.container_name, partition_key=self.partition_key_path
)
print("CosmosCRUD: Asynchronous resources initialized successfully.")
async def create_item(self, item: dict):
"""示例:创建Cosmos DB文档"""
if not self.container:
raise RuntimeError("Cosmos container not initialized.")
print(f"CosmosCRUD: Creating item with id '{item.get('id')}'...")
return await self.container.create_item(item)
async def read_item(self, item_id: str, partition_key_value: str):
"""示例:读取Cosmos DB文档"""
if not self.container:
raise RuntimeError("Cosmos container not initialized.")
print(f"CosmosCRUD: Reading item '{item_id}'...")
return await self.container.read_item(item_id, partition_key=partition_key_value)
async def close(self):
"""清理资源,例如关闭CosmosClient连接"""
if self.client:
print("CosmosCRUD: Closing CosmosClient...")
await self.client.close()
self.client = None # 清理引用
print("CosmosClient closed.")
# --- 模拟CosmosClient及其相关代理类以便于运行示例 ---
class MockContainerProxy:
def __init__(self, name):
self.name = name
async def create_item(self, item):
print(f"Mock: Created item {item.get('id')} in container '{self.name}'")
return item
async def read_item(self, item_id, partition_key):
print(f"Mock: Reading item '{item_id}' from container '{self.name}' (pk: {partition_key})")
return {"id": item_id, "data": "mock_data", "pk": partition_key}
class MockDatabaseProxy:
def __init__(self, name):
self.name = name
async def create_container_if_not_exists(self, name, partition_key):
print(f"Mock: Creating/getting container '{name}' (partition_key: {partition_key})")
return MockContainerProxy(name)
class MockCosmosClient:
async def create_database_if_not_exists(self, name):
print(f"Mock: Creating/getting database '{name}'")
return MockDatabaseProxy(name)
async def close(self):
print("Mock: CosmosClient closed.")
# --- 示例用法 ---
async def main():
print("\n--- Starting main execution ---")
mock_client = MockCosmosClient()
# 使用异步工厂方法创建CosmosCRUD实例
print("\nAttempting to create CosmosCRUD instance...")
cosmos_crud_instance = await CosmosCRUD.create(
client=mock_client,
database_name="MyFastAPIDB",
container_name="UserItems",
partition_key_path="/id"
)
print("CosmosCRUD instance successfully created and initialized.")
# 现在可以使用实例的异步方法
new_item = {"id": "user123", "name": "Alice", "city": "New York"}
await cosmos_crud_instance.create_item(new_item)
retrieved_item = await cosmos_crud_instance.read_item("user123", "user123")
print(f"Retrieved item: {retrieved_item}")
# 清理资源
await cosmos_crud_instance.close()
print("--- Main execution finished ---")
if __name__ == "__main__":
asyncio.run(main())优点:
在FastAPI等Web框架中,依赖注入(Dependency Injection, DI)是一种强大的模式,用于管理和提供请求处理函数所需的资源。FastAPI的依赖项可以被定义为async函数,从而能够异步地获取数据库连接、验证用户等。
虽然依赖注入可以解决如何将一个已初始化的CosmosCRUD实例提供给FastAPI路由的问题,但它通常不直接解决CosmosCRUD类自身内部的异步初始化问题。如果CosmosCRUD实例在被创建后需要执行异步操作来设置其内部状态(如创建数据库/容器),那么异步工厂模式仍然是类设计的首选。
例如,在FastAPI中,你可能会这样使用:
from fastapi import FastAPI, Depends
# ... 其他导入和 CosmosCRUD 类定义 ...
app = FastAPI()
# 异步依赖项,用于创建并提供CosmosCRUD实例
async def get_cosmos_crud_instance():
mock_client = MockCosmosClient() # 实际应用中会是真实的CosmosClient
cosmos_crud = await CosmosCRUD.create(
client=mock_client,
database_name="MyFastAPIDB",
container_name="UserItems",
partition_key_path="/id"
)
try:
yield cosmos_crud
finally:
await cosmos_crud.close() # 确保资源被清理
@app.get("/users/{user_id}")
async def read_user(user_id: str, cosmos: CosmosCRUD = Depends(get_cosmos_crud_instance)):
user = await cosmos.read_item(user_id, user_id)
return {"user": user}
# 运行FastAPI应用: uvicorn your_module_name:app --reload在这种场景下,FastAPI的依赖注入机制与异步工厂模式完美结合,实现了异步资源的生命周期管理。
在Python中,__init__方法不能直接包含异步操作。为了在类实例化时进行异步资源初始化,最推荐且优雅的解决方案是采用异步工厂方法。这种模式通过一个同步的__init__方法进行属性声明,并结合一个async类方法来执行所有的异步设置,从而确保了代码的规范性、可读性、可维护性,并避免了性能问题和IDE警告。结合FastAPI等框架的依赖注入机制,可以实现异步资源的高效且生命周期可控的管理。
以上就是Python异步类构造器的最佳实践:避免__init__中的异步操作的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号