Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I properly decorate a `classmethod` with `functools.lru_cache`?

I tried to decorate a classmethod with functools.lru_cache. My attempt failed:

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

obj = K()

The error message comes from functools.lru_cache:

TypeError: the first argument must be callable
like image 259
Samuel Muldoon Avatar asked Oct 31 '19 18:10

Samuel Muldoon


2 Answers

A class method is, itself, not callable. (What is callable is the object return by the class method's __get__ method.)

As such, you want the function decorated by lru_cache to be turned into a class method instead.

@classmethod
@functools.lru_cache(maxsize=32)
def mthd(cls, stryng: str):
    return stryng
like image 127
chepner Avatar answered Nov 07 '22 12:11

chepner


The selected answer is totally correct but adding another post. If you want to bind the cache storages to each classes, instead of sharing the single storage to all its subclasses, there is another option 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')

Then the result is

$ python example.py
functools 1
functools 2
methodtools 1
methodtools 2
functools 1
functools 2
like image 2
youknowone Avatar answered Nov 07 '22 12:11

youknowone