讲师中心 微信公众号
AI工具推荐 视频效率加速

Python异步类构造器设计模式:避免在__init__中执行异步操作

雨墨姑娘_1911

雨墨姑娘_1911

发布时间:2025-12-05 14:10:12

|

359人浏览过

|

来源于php中文网

原创

Python异步类构造器设计模式:避免在__init__中执行异步操作

在python异步编程的实践中,尤其是在构建需要与异步i/o资源(如数据库、网络服务)交互的类时,开发者常常会遇到一个棘手的问题:如何在类的构造函数`__init__`中执行异步操作,例如初始化数据库连接或确保资源存在?由于`__init__`方法本质上是一个同步函数,它不允许直接使用`await`关键字,这使得许多期望在对象创建时完成异步初始化的尝试都以失败告终。

理解问题:为什么__init__不能是异步的?

Python的async/await语法糖是为协程(coroutine)设计的,协程需要在事件循环中调度执行。而__init__方法是普通函数,它在对象实例化时同步执行,不参与事件循环。因此,尝试在__init__中直接使用await会导致SyntaxError。

考虑以下一个尝试在__init__中初始化Cosmos DB客户端的示例,它旨在确保数据库和容器在类实例创建时就已经准备就绪:

import asyncio
from azure.cosmos.aio import CosmosClient # 假设这是一个异步客户端

class CosmosCRUD:
    def __init__(self, client: CosmosClient):
        self.client = client
        # 以下代码会导致SyntaxError,因为__init__不是async函数
        # 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={"kind": "Hash", "paths": ["/id"]})

上述代码由于await的使用,将无法运行。

不推荐的解决方案及其弊端

面对__init__的同步限制,开发者可能会考虑以下几种替代方案,但它们通常伴随着严重的性能或设计问题:

立即学习“Python免费学习笔记(深入)”;

1. 在__init__中创建新的事件循环并运行异步代码

这种方法试图在__init__内部通过asyncio.run()或loop.run_until_complete()来同步地执行异步初始化逻辑。

import asyncio
from azure.cosmos.aio import CosmosClient

class CosmosCRUDBad:
    def __init__(self, client: CosmosClient):
        self.client = client
        loop = asyncio.get_event_loop() # 或者创建一个新的loop
        # 这种方式会阻塞当前线程,直到异步操作完成
        self.database = loop.run_until_complete(
            self.client.create_database_if_not_exists("MY_DATABASE_NAME")
        )
        self.container = loop.run_until_complete(
            self.database.create_container_if_not_exists("MY_CONTAINER_NAME", partition_key={"kind": "Hash", "paths": ["/id"]})
        )

弊端:

python 查询技能
python 查询技能

查询客流数据,输出JSON格式,可直接导入Bitable等可视化工具

下载
  • 性能瓶颈: 如果此类实例在请求处理路径上频繁创建(例如,在每个FastAPI请求中),那么每次创建实例都会阻塞当前的事件循环或创建一个新的事件循环并等待其完成。这会严重损害应用程序的并发处理能力,导致性能急剧下降。
  • 资源管理复杂: 创建和管理内部事件循环会增加复杂性,并可能导致资源泄露或死锁。
  • 反模式: 违背了异步编程的核心原则,即避免阻塞事件循环。

2. 依赖注入(Dependency Injection)

虽然依赖注入是一种优秀的设计模式,用于管理组件间的依赖关系,但它本身并不能直接解决在__init__中执行异步初始化逻辑的问题。如果被注入的依赖本身需要异步初始化,那么问题只是转移了,而不是解决了。例如,如果你注入的是一个已经完全初始化的Cosmos DB容器对象,那么这个容器对象的初始化过程仍然需要在某个异步上下文中完成。

推荐的解决方案:异步工厂方法模式

最佳实践是保持__init__方法的轻量和同步,仅用于设置实例的基本属性,而将所有异步初始化逻辑封装在一个单独的异步工厂方法中。

核心思想

  • __init__方法只负责接收必要的参数并初始化同步属性。
  • 创建一个async classmethod(或async staticmethod),作为类的“异步构造器”。这个方法负责执行所有异步初始化操作,然后创建一个类的实例并返回。

示例代码

以下是使用异步工厂方法模式重构CosmosCRUD类的示例:

import asyncio
from azure.cosmos.aio import CosmosClient, DatabaseProxy, ContainerProxy

class CosmosCRUD:
    def __init__(self, client: CosmosClient, database: DatabaseProxy, container: ContainerProxy):
        """
        同步构造函数,仅用于接收已初始化的依赖。
        """
        self.client = client
        self.database = database
        self.container = container
        print("CosmosCRUD 实例已同步创建。")

    @classmethod
    async def create(cls, client: CosmosClient, db_name: str, container_name: str, partition_key_path: str = "/id"):
        """
        异步工厂方法,负责所有异步初始化逻辑。
        """
        print(f"正在异步初始化 CosmosCRUD 实例...")
        database = await client.create_database_if_not_exists(db_name)
        container = await database.create_container_if_not_exists(
            container_name, partition_key={"kind": "Hash", "paths": [partition_key_path]}
        )
        # 初始化完成后,调用同步构造函数创建并返回实例
        return cls(client, database, container)

    async def create_item(self, item: dict):
        """
        示例:异步创建文档
        """
        response = await self.container.create_item(item, enable_automatic_id_generation=True)
        print(f"创建项成功: {response['id']}")
        return response

    async def read_item(self, item_id: str, partition_key: str):
        """
        示例:异步读取文档
        """
        response = await self.container.read_item(item_id, partition_key=partition_key)
        print(f"读取项成功: {response['id']}")
        return response

# 如何使用这个类
async def main():
    # 假设 client 已经是一个异步 CosmosClient 实例
    # 通常在应用启动时创建一次
    # client = CosmosClient(url="YOUR_COSMOS_URL", credential="YOUR_COSMOS_KEY")
    # 为了演示,我们使用一个模拟的客户端
    class MockCosmosClient:
        async def create_database_if_not_exists(self, name):
            print(f"模拟:创建或获取数据库 '{name}'")
            return MockDatabaseProxy(name)
    class MockDatabaseProxy:
        def __init__(self, name): self.name = name
        async def create_container_if_not_exists(self, name, partition_key):
            print(f"模拟:创建或获取容器 '{name}' (数据库: {self.name})")
            return MockContainerProxy(name)
    class MockContainerProxy:
        def __init__(self, name): self.name = name
        async def create_item(self, item, enable_automatic_id_generation):
            print(f"模拟:创建项 {item}")
            return {"id": "mock_id_123", **item}
        async def read_item(self, item_id, partition_key):
            print(f"模拟:读取项 {item_id} (分区键: {partition_key})")
            return {"id": item_id, "data": "some_data", "pk": partition_key}

    mock_client = MockCosmosClient()

    # 使用异步工厂方法创建实例
    crud_manager = await CosmosCRUD.create(
        client=mock_client,
        db_name="MyAwesomeDB",
        container_name="MyContainer",
        partition_key_path="/pk"
    )

    # 现在可以使用 crud_manager 实例进行异步操作
    new_item = await crud_manager.create_item({"pk": "category1", "name": "Product A"})
    await crud_manager.read_item(new_item["id"], "category1")

if __name__ == "__main__":
    asyncio.run(main())

优点:

  • 清晰的分离: __init__负责同步初始化,create方法负责异步初始化,职责明确。
  • 避免阻塞: 异步初始化逻辑在事件循环中执行,不会阻塞主线程或事件循环。
  • 易于理解和维护: 代码结构更符合异步编程范式。
  • IDE友好: 尽管PyCharm等IDE可能对通过工厂方法创建的实例变量发出警告(因为它们不在__init__中直接赋值),但通过在__init__中声明类型提示(如self.database: DatabaseProxy),可以大大缓解这些警告,并提供更好的代码补全。

结合 FastAPI 的应用场景

在 FastAPI 项目中,这种模式与依赖注入(Dependency Injection)机制结合使用效果更佳。你可以在应用启动时(例如使用lifespan事件)创建并初始化一次CosmosCRUD实例,然后将其作为全局依赖提供给路由函数。

from fastapi import FastAPI, Depends
from contextlib import asynccontextmanager

# 假设 CosmosClient 已经初始化
# cosmos_client = CosmosClient(...)

@asynccontextmanager
async def lifespan(app: FastAPI):
    # 应用程序启动时运行
    print("应用启动中...")
    global crud_manager_instance
    # 异步创建 CosmosCRUD 实例
    crud_manager_instance = await CosmosCRUD.create(
        client=cosmos_client,
        db_name="MyAwesomeDB",
        container_name="MyContainer",
        partition_key_path="/pk"
    )
    yield
    # 应用程序关闭时运行
    print("应用关闭中...")
    # 这里可以添加资源清理逻辑,例如关闭 CosmosClient
    # await cosmos_client.close()

app = FastAPI(lifespan=lifespan)

# 依赖函数,提供 CosmosCRUD 实例
async def get_cosmos_crud() -> CosmosCRUD:
    return crud_manager_instance

@app.get("/items/{item_id}")
async def read_item_route(item_id: str, pk: str, crud: CosmosCRUD = Depends(get_cosmos_crud)):
    item = await crud.read_item(item_id, pk)
    return {"message": "Item retrieved", "item": item}

@app.post("/items/")
async def create_item_route(item_data: dict, crud: CosmosCRUD = Depends(get_cosmos_crud)):
    new_item = await crud.create_item(item_data)
    return {"message": "Item created", "item": new_item}

注意事项:

  • 在lifespan事件中创建的全局实例,需要确保其线程安全性和生命周期管理。
  • 对于需要清理的异步资源(如CosmosClient),应在lifespan的yield之后执行清理操作。

总结

在Python异步编程中,避免在类的__init__方法中执行异步操作是至关重要的设计原则。通过采用异步工厂方法模式,我们可以清晰地分离同步实例化和异步初始化逻辑,从而构建出高性能、可维护且符合异步范式的应用程序。这种模式不仅解决了await在__init__中的限制,也提升了代码的整体质量和可读性。

热门AI工具

更多
二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

AionClaw
AionClaw Hot

AionClaw是一款面向办公、创作和编程任务的AI桌面智能体。

讯飞绘文

讯飞绘文是一款由科大讯飞推出的一站式 AIGC 内容运营平台。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

UP简历
UP简历 Hot

一款AI办公效率工具,主要用于基于AI技术的免费在线简历制作工具,适合需要提升相关任务效率的用户。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

相关专题

更多
Python FastAPI异步API开发_Python怎么用FastAPI构建异步API
Python FastAPI异步API开发_Python怎么用FastAPI构建异步API

Python FastAPI 异步开发利用 async/await 关键字,通过定义异步视图函数、使用异步数据库库 (如 databases)、异步 HTTP 客户端 (如 httpx),并结合后台任务队列(如 Celery)和异步依赖项,实现高效的 I/O 密集型 API,显著提升吞吐量和响应速度,尤其适用于处理数据库查询、网络请求等耗时操作,无需阻塞主线程。

119

2025.12.22

Python 微服务架构与 FastAPI 框架
Python 微服务架构与 FastAPI 框架

本专题系统讲解 Python 微服务架构设计与 FastAPI 框架应用,涵盖 FastAPI 的快速开发、路由与依赖注入、数据模型验证、API 文档自动生成、OAuth2 与 JWT 身份验证、异步支持、部署与扩展等。通过实际案例,帮助学习者掌握 使用 FastAPI 构建高效、可扩展的微服务应用,提高服务响应速度与系统可维护性。

554

2026.02.06

Python Web框架FastAPI 全栈开发教程合集
Python Web框架FastAPI 全栈开发教程合集

以 FastAPI 为核心,讲解现代 Python Web API 的高效开发方式,涵盖路由定义与路径参数/查询参数/请求体绑定、Pydantic 模型的数据校验与序列化、依赖注入(Depends)系统的分层设计、中间件与 CORS 配置、OAuth2 + JWT 认证流程、后台任务(BackgroundTasks)、WebSocket 实时通信、SQLAlchemy 异步 ORM 集成、自动生成 OpenAPI/Swagger 交互文

556

2026.05.09

Python FastAPI异步微服务与高性能接口设计
Python FastAPI异步微服务与高性能接口设计

本专题聚焦 Python FastAPI 框架在高性能接口与微服务开发中的应用,讲解异步请求处理、依赖注入机制、路由设计、数据库异步操作以及接口性能优化策略。结合实际项目案例,帮助开发者构建高并发、低延迟的现代化后端服务架构。

459

2026.06.16

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

4018

2023.08.10

pycharm怎么改成中文
pycharm怎么改成中文

PyCharm是一种Python IDE(Integrated Development Environment,集成开发环境),带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具,比如调试、语法高亮、项目管理、代码跳转、智能提示、自动完成、单元测试、版本控制。此外,该IDE提供了一些高级功能,以用于支持Django框架下的专业Web开发。php中文网给大家带来了pycharm相关的教程以及文章,欢迎大家前来学习和阅读。

2769

2023.07.25

pycharm安装教程
pycharm安装教程

PyCharm是一款由JetBrains开发的Python集成开发环境(IDE),它提供了许多方便的功能和工具。本专题为大家带来pycharm安装教程,帮助大家解决问题。

5217

2023.08.21

如何解决pycharm找不到模块
如何解决pycharm找不到模块

解决pycharm找不到模块的方法:1、检查python解释器;2、安装缺失的模块;3、检查项目结构;4、检查系统路径;5、使用虚拟环境;6、重启PyCharm或电脑。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

738

2023.12.04

PixTV官网入口地址合集
PixTV官网入口地址合集

本专题汇总了 PixTV AI 一站式视频创作平台的官方入口与使用教程。无需下载软件,浏览器直接访问即可使用。平台将剧本、图像、视频、声音与剪辑整合在“无限画布”中,接入 GPT Image 2.5、Seedance 2.5 等头部模型。本专题整理了从新建画布、角色锚定、分镜拆分到视频生成与导出的完整操作指南,助你快速上手 AI 短剧与漫剧创作。

80

2026.10.10

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn