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

求类似functools.cached_property的方法缓存装饰器流行实现

实现类似functools.cached_property的方法缓存装饰器(无需对象可哈希)

如果你需要给类方法实现和cached_property逻辑一致的缓存——将缓存数据存在对象自身的__dict__中,而非全局字典,从而允许对象不可哈希——下面是几种可靠的方案:

一、手动实现轻量版装饰器

这是最灵活的方案,完全符合需求,且无需依赖任何第三方库:

import functools

def cached_method(func):
    @functools.wraps(func)
    def wrapper(self, *args, **kwargs):
        # 为当前对象初始化专属缓存字典
        if not hasattr(self, '_cached_method_store'):
            self._cached_method_store = {}
        
        # 生成唯一缓存键:结合方法名、位置参数和关键字参数
        # 用frozenset处理kwargs,保证无序的kwargs能生成一致的键
        cache_key = (func.__qualname__, args, frozenset(kwargs.items()))
        
        if cache_key not in self._cached_method_store:
            self._cached_method_store[cache_key] = func(self, *args, **kwargs)
        return self._cached_method_store[cache_key]
    return wrapper

用法示例

class UnhashableObj:
    def __init__(self, base_val):
        self.base_val = base_val
        # 手动禁用对象的哈希能力,模拟不可哈希场景
        self.__hash__ = None

    @cached_method
    def calculate(self, multiplier):
        print("执行计算逻辑...")
        return self.base_val * multiplier

# 测试不可哈希对象的缓存效果
obj_a = UnhashableObj(5)
print(obj_a.calculate(2))  # 输出:执行计算逻辑... 10
print(obj_a.calculate(2))  # 直接返回缓存值10,无计算输出

obj_b = UnhashableObj(3)
print(obj_b.calculate(2))  # 输出:执行计算逻辑... 6

这个装饰器的核心逻辑是:给每个对象单独维护一个缓存字典,缓存键由方法标识和调用参数组合而成,完全避免了对对象哈希性的要求。

二、使用流行第三方库的现成实现

如果你的项目已经依赖一些主流类工具库,直接用它们的内置实现更省心:

1. attrs库的@cached_method

attrs是Python生态中非常流行的类简化库,自带的@cached_method装饰器完全满足你的需求——缓存数据存储在对象自身,无需对象可哈希,且经过了充分的测试优化。

首先安装库:

pip install attrs

用法示例

from attrs import define, cached_method

@define
class UnhashableObj:
    base_val: int
    # 禁用哈希
    __hash__ = None

    @cached_method
    def calculate(self, multiplier: int):
        print("执行计算逻辑...")
        return self.base_val * multiplier

attrs的cached_method内部逻辑和手动实现类似,但额外处理了一些边界情况(比如方法继承、参数类型兼容性等),稳定性更高。

为什么不用functools.cache/lru_cache?

functools模块的这两个缓存装饰器,其缓存字典是绑定在装饰器闭包中的,缓存键需要包含对象实例本身,因此要求对象必须可哈希。而我们的方案将缓存数据存在对象自身的属性中,从根源上避开了这个限制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 18:19:53