メソッドのキャッシュには cached_method が便利

以前の記事で、functools.cache をそのままメソッドに使うとメモリリークになることと、その回避方法をいくつか紹介しました。

最後に紹介していた方法がこれです。

from functools import cache

class A:
    def __init__(self, x):
        self._x = x
        self.f = cache(self._f)

    def _f(self, y):
        return self._x * y

このコードでは A.f() が呼ばれていない時も cache(self._f) が実行されているので、 cached_property を使えばさらに改善ができます。

from functools import cache, cached_property

class A:
    def __init__(self, x):
        self._x = x

    @cached_property
    def f(self):
        return cache(self._f)

    def _f(self, y):
        return self._x * y

しかしキャッシュが必要になるたびにこのコードを書くのは面倒ですよね。それをしてくれるのが cached_method です。 uv add cached_method でインストールできます。上の例に cached_method を使うとこうなります。

from cached_method import cached_method

class A:
    def __init__(self, x):
        self._x = x

    @cached_method
    def f(self, y):
        return self._x * y

メソッドのためのcacheでコレータとしては cachetoolscachedmethod もありましたが、実装が functools.lru_cache と異なるものになるし、メソッドごとにキャッシュのdictとそれを取得するための関数を用意する必要があり面倒でした。一方で typing サポートは cachetools の方がstubがあるので痛し痒しといったところです。

このブログに乗せているコードは引用を除き CC0 1.0 で提供します。