Skip to content

プロパティのキャッシュ(cached_property)

@cached_property をデコレーターとして付けておけば、メソッドの返り値をキャッシュできる。 1回目に計算した結果を覚えておいて、2回目以降は呼び出された瞬間にキャッシュ値を返す。

cf. https://docs.python.org/ja/3/library/functools.html#functools.cached_property

このページでは挙動を実験で確かめる。 JWT(RS256/EdDSA) では、鍵ファイルの読み込み結果をキャッシュするのに使った。 Pydantic の設定(設定管理)のような「一度読んだら変わらない値」とも相性がいい。


実験1: cached_property を使う

add_numbers@cached_property を付けて、 10回呼んでみる。中の print(計算中の合図)が何回出るかを見る。

# main.py
from functools import cached_property


class Calculator:
    @cached_property
    def add_numbers(self):
        a, b = 2, 3
        x = a + b
        print(f"【計算中!】 {x}")  # これが何回出るか?
        return x


calc = Calculator()

for i in range(10):
    print(f"{i + 1}回目: {calc.add_numbers}")

% uv run python main.py
【計算中!】 5
1回目: 5
2回目: 5
3回目: 5
4回目: 5
5回目: 5
6回目: 5
7回目: 5
8回目: 5
9回目: 5
10回目: 5

【計算中!】 は最初の1回だけ。 2回目以降はキャッシュ値を返すので、計算(と print)は走っていない。

Note

@cached_property を付けると、メソッドではなくプロパティになるので、呼び出しは calc.add_numbers() ではなく calc.add_numbers() なし)になる。


実験2: cached_property を使わない

比較のために、 @cached_property を外してただのメソッドにする。

# main.py
class Calculator:
    def add_numbers(self):
        a, b = 2, 3
        x = a + b
        print(f"【計算中!】 {x}")  # これが何回出るか?
        return x


calc = Calculator()

for i in range(10):
    print(f"{i + 1}回目: {calc.add_numbers()}")

% uv run python main.py
【計算中!】 5
1回目: 5
【計算中!】 5
2回目: 5
【計算中!】 5
3回目: 5
【計算中!】 5
4回目: 5
【計算中!】 5
5回目: 5
【計算中!】 5
6回目: 5
【計算中!】 5
7回目: 5
【計算中!】 5
8回目: 5
【計算中!】 5
9回目: 5
【計算中!】 5
10回目: 5

今度は 【計算中!】 が毎回出ている。呼ぶたびに計算し直しているので、重い処理(ファイル読み込みや鍵の復元など)だと無駄が大きい。そういうときに @cached_property が効く。