You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python如何在上下文管理器__exit__中获取__enter__方法的返回值

方案1:使用独立上下文包装对象(无并发风险,推荐)

不要直接在连接池类上实现__enter__/__exit__,每次调用时生成独立的上下文管理器实例,每个实例单独存储本次申请的连接,完全避免多线程/嵌套调用的冲突问题:

from psycopg2.pool import ThreadedConnectionPool

class ManagedConnectionPool:
    def __init__(self, *args, **kwargs):
        self._pool = ThreadedConnectionPool(*args, **kwargs)
    
    def __call__(self):
        pool = self._pool
        # 每次调用返回独立的上下文对象
        class PoolConnectionContext:
            def __enter__(self):
                self.conn = pool.getconn()
                return self.conn
            def __exit__(self, exc_type, exc_val, exc_tb):
                if self.conn:
                    # 异常自动回滚事务,避免脏连接放回池
                    if exc_type is not None:
                        self.conn.rollback()
                    pool.putconn(self.conn)
        return PoolConnectionContext()

使用方式只需加一对括号即可:

pool = ManagedConnectionPool(minconn=1, maxconn=10, dbname="your_db", user="your_user")
with pool() as conn:
    with conn.cursor() as cur:
        # 执行SQL操作
        cur.execute("SELECT 1")
        print(cur.fetchone())

方案2:使用线程本地存储(贴合你需要的语法)

如果要严格实现with pool as conn的写法,可以借助threading.local()存储当前线程持有的连接,不同线程的存储完全隔离,不会出现连接串用的问题:

import threading
from psycopg2.pool import ThreadedConnectionPool

class ContextThreadedConnectionPool(ThreadedConnectionPool):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 线程本地存储,每个线程独立持有自己的连接引用
        self._thread_local = threading.local()
    
    def __enter__(self):
        conn = self.getconn()
        self._thread_local.current_conn = conn
        return conn
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        conn = getattr(self._thread_local, 'current_conn', None)
        if conn:
            if exc_type is not None:
                conn.rollback()
            self.putconn(conn)
            # 清理本地存储避免残留引用
            del self._thread_local.current_conn

使用方式完全符合你的预期:

pool = ContextThreadedConnectionPool(minconn=1, maxconn=10, dbname="your_db", user="your_user")
with pool as conn:
    with conn.cursor() as cur:
        # 执行SQL操作
        ...

两种方案都可以在多线程环境下安全运行,第一种方案兼容性更强,就算同一线程内同时申请多个池连接也不会冲突,更推荐使用。

内容的提问来源于stack exchange,提问作者winwin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 21:45:01