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

Python协程中使用lru_cache报错的原因及实现方法

协程中使用lru_cache报错的原因及解决方法

错误原因

functools.lru_cache 是为同步函数设计的装饰器,它直接缓存被装饰异步函数返回的协程对象,而非协程执行后的结果:

  • 首次调用时,你await该协程对象获取到结果;
  • 第二次从缓存取出的是已经被await过的协程对象,而协程对象是一次性的,无法重复执行,因此触发 RuntimeError: cannot reuse already awaited coroutine。

解决方法

方法1:手动实现结果缓存

自己维护缓存字典,直接缓存协程执行后的结果,同时控制缓存大小:

def __init__(self):
    self._company_id_cache = {}

async def get_company_id(self, simulation_id: int):
    if simulation_id in self._company_id_cache:
        return self._company_id_cache[simulation_id]
    
    simulation_in_db = await self.get_by_id(_id=simulation_id)
    if not simulation_in_db:
        raise ValueError("Simulation not found")
    
    company_id = simulation_in_db["company_id"]
    self._company_id_cache[simulation_id] = company_id
    
    # 控制缓存最大数量为8,超过则删除最早的条目
    if len(self._company_id_cache) > 8:
        oldest_key = next(iter(self._company_id_cache.keys()))
        del self._company_id_cache[oldest_key]
    
    return company_id

方法2:使用专门的异步缓存库async-lru

async-lru是专为异步函数设计的缓存装饰器,用法和lru_cache类似:

  1. 先安装库:
pip install async-lru
  1. 修改代码:
from async_lru import alru_cache

@alru_cache(maxsize=8)
async def get_company_id(self, simulation_id: int):
    simulation_in_db = await self.get_by_id(_id=simulation_id)
    if not simulation_in_db:
        raise ValueError("Simulation not found")
    company_id = simulation_in_db["company_id"]
    return company_id

方法3:自定义异步缓存装饰器(不依赖第三方库)

手动实现一个装饰器,缓存协程的执行结果而非协程对象:

import functools
import asyncio

def async_lru_cache(maxsize=128):
    def decorator(func):
        cache = functools.lru_cache(maxsize=maxsize)
        
        @functools.wraps(func)
        async def wrapper(*args, **kwargs):
            key = (args, frozenset(kwargs.items()))
            future = cache.get(key)
            
            if future is None:
                future = asyncio.create_task(func(*args, **kwargs))
                cache(*args, **kwargs)
            
            try:
                return await future
            except Exception:
                # 任务执行失败时清除缓存,下次重新尝试
                cache.cache_clear()
                raise
        
        return wrapper
    return decorator

# 使用自定义装饰器
@async_lru_cache(maxsize=8)
async def get_company_id(self, simulation_id: int):
    simulation_in_db = await self.get_by_id(_id=simulation_id)
    if not simulation_in_db:
        raise ValueError("Simulation not found")
    company_id = simulation_in_db["company_id"]
    return company_id

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 11:26:30