プロパティのキャッシュ(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 が効く。