Pythonのキャッシュ機能(functools.cache)を使ってみた
functools.cacheの概要
functools
とは関数を操作するためのPythonの標準モジュールです。
functools.cache
はPython 3.9から導入され、デコレータ[1]として機能します。
functools.cache
を使用することで、関数の引数と戻り値をキャッシュし、再度同一の引数で関数を呼び出すとすぐに戻り値を得ることができます。
ちなみに、functools.cache
はfunctools.lru_cache(maxsize=None)
[2]と同等です。
階乗の計算でfunctools.cacheの挙動を確認してみた
functools.cache
のドキュメントに記載されている階乗計算プログラムにて挙動を確認してみます。
# functoolsモジュールをインポート
import functools
# デコレータfunctools.cacheを使用
@functools.cache
# 階乗n!を計算する関数
def factorial(n):
# nを標準出力
print("n = " + str(n))
# nが0の場合は1を返す
# nが0以外の場合はn*factorial(n-1)を返す
return n * factorial(n-1) if n else 1
# factorial(10)を実行
print("factorial(10)")
factorial(10)
# factorial(5)を実行
print("factorial(5)")
factorial(5)
# factorial(12)を実行
print("factorial(12)")
factorial(12)
上記のf.py
を実行すると以下のようになります。
C:\>py f.py
factorial(10)
n = 10
n = 9
n = 8
n = 7
n = 6
n = 5
n = 4
n = 3
n = 2
n = 1
n = 0
factorial(5)
factorial(12)
n = 12
n = 11
C:\>
factorial(10)
ではfactorial(10)
~factorial(0)
を呼び出して実行するため、n = 10
~n = 0
が出力されています。
factorial(5)
は前述のfactorial(10)
の実行によって結果がキャッシュされているため、即座に戻り値を得ることができます。
そのため、n = 5
~n = 0
は出力されません。
factorial(12)
は結果がキャッシュされていないfactorial(12)
とfactorial(11)
のみ実行することになるため、n = 12
とn = 11
のみ出力されます。
関数の引数はハッシュ可能でなければならない
以下のPythonプログラムをご覧ください。
# functoolsモジュールをインポート
import functools
# デコレータfunctools.cacheを使用
@functools.cache
# リストの要素を標準出力する関数
def print_list(l):
for item in l:
print(item)
# リストを定義
l = ["a", "b", "c"]
# print_list関数を実行
print_list(l)
リストの各要素を標準出力する簡単なプログラムです。
このプログラムを実行してみます。
C:\>py unhashable.py
Traceback (most recent call last):
File "C:\unhashable.py", line 14, in <module>
print_list(l)
TypeError: unhashable type: 'list'
C:\>
functools.cache
デコレータを適用する場合、引数はハッシュ可能でなければなりません。
今回、print_list
関数の引数はハッシュ可能でないリスト型となっていたため、エラーが発生しました。
次は、引数をハッシュ可能なタプル型にしてみましょう。
# functoolsモジュールをインポート
import functools
# デコレータfunctools.cacheを使用
@functools.cache
# タプルの要素を標準出力する関数
def print_list(t):
for item in t:
print(item)
# タプルを定義
t = ("a", "b", "c")
# print_list関数を実行
print_list(t)
このプログラムを実行してみます。
C:\>py test.py
a
b
c
C:\>
正常に実行することができました。
まとめ
-
functools.cache
を使用すると、関数の引数と戻り値をキャッシュすることができるため、実行時間を短縮できます。 -
functools.cache
の適用対象はハッシュ可能な引数をとる関数でなければなりません。
-
関数のデコレータとは関数を別の関数に変換する関数です。ゲシュタルト崩壊しそうです…。 ↩︎
-
functools.lru_cache(maxsize=N)
は直近N回分の関数呼び出しの戻り値をキャッシュするデコレータです。maxsize=None
とした場合、回数制限が無くなります。 ↩︎
Discussion