如何用 `functools.lru_cache` 正确装饰`classmethod`?

Posted

技术标签:

【中文标题】如何用 `functools.lru_cache` 正确装饰`classmethod`?【英文标题】:How do I properly decorate a `classmethod` with `functools.lru_cache`? 【发布时间】:2020-02-27 04:16:15 【问题描述】:

我尝试用functools.lru_cache 装饰classmethod。我的尝试失败了:

import functools
class K:
    @functools.lru_cache(maxsize=32)
    @classmethod
    def mthd(i, stryng: str): \
        return stryng

obj = K()

错误信息来自functools.lru_cache:

TypeError: the first argument must be callable

【问题讨论】:

classmethod 必须最后使用.. 这意味着它必须位于装饰器链的顶部 【参考方案1】:

类方法本身是不可调用的。 (可调用的是类方法的__get__方法返回的对象。)

因此,您希望将lru_cache 修饰的函数转换为类方法。

@classmethod
@functools.lru_cache(maxsize=32)
def mthd(cls, stryng: str):
    return stryng

【讨论】:

【参考方案2】:

所选答案完全正确,但要添加另一个帖子。如果您想将缓存存储绑定到每个类,而不是将单个存储共享给它的所有子类,还有另一个选项methodtools

import functools
import methodtools


class K:
    @classmethod
    @functools.lru_cache(maxsize=1)
    def mthd(cls, s: str):
        print('functools', s)
        return s

    @methodtools.lru_cache(maxsize=1)  # note that methodtools wraps classmethod
    @classmethod
    def mthd2(cls, s: str):
        print('methodtools', s)
        return s


class L(K):
    pass


K.mthd('1')
L.mthd('2')
K.mthd2('1')
L.mthd2('2')

K.mthd('1')  # functools share the storage
L.mthd('2')
K.mthd2('1')  # methodtools doesn't share the storage
L.mthd2('2')

那么结果就是

$ python example.py
functools 1
functools 2
methodtools 1
methodtools 2
functools 1
functools 2

【讨论】:

以上是关于如何用 `functools.lru_cache` 正确装饰`classmethod`?的主要内容,如果未能解决你的问题,请参考以下文章

将 functools.lru_cache 应用于 lambda

functools.lru_cache的实现

在 Python >= 3.2 中将缓存存储到文件 functools.lru_cache

如何使用 Python 装饰器以便方法使用 functools.lru_cache 并自行注册?

Python 函数缓存(qbit)

python的functools模块