この記事では、Pythonで関数をキャッシュして2度目以降の呼び出しを高速化する方法を解説します。
よく使う関数や重い関数なんかはキャッシュしておくことで高速化することができます。
それでは、関数のキャッシュ方法を見ていきましょう❗️
関数をキャッシュする方法
関数をキャッシュするには、Python 3.9で追加されたfunctools
モジュールのcache
デコレータを使うことでめちゃくちゃ簡単に実装できます。
では、使ってみましょう!
import time
# 関数の実行時間を測るデコレータ
def tictoc(func):
def _wrapper(*args, **keywargs):
start_time = time.time()
result = func(*args, **keywargs)
print('time: {:.9f} [sec]'.format(time.time() - start_time))
return result
return _wrapper
# ここからが本題
import functools
@tictoc
@functools.cache # デコレータを付与
def func(num):
# なんらかの重い処理
for _ in range(1000000):
num += 1
return num
# 1度目の呼び出し
func(1)
# 2度目の呼び出し
func(1)
実行結果
time: 0.117470980 [sec]
time: 0.000002146 [sec]
2度目の呼び出しがめちゃくちゃ早くなりました!
引数でキャッシュする
キャッシュには、辞書が使われています。keyに関数に渡された引数、valueに関数の結果を格納しています。
なので、引数はハッシュ可能な値でなければならず、キーワード引数の順序が異なる場合は、異なるキャッシュとして保存されます。
例えばf(a=1, b=2)
とf(b=2, a=1)
は別。
引数が同じなら同じ値を返す関数に使う
同じ引数を指定しても同じ値を返さない関数をキャッシュしてしまうと、2回目以降に同じ値を返すようになってしまい、その関数の本来の結果を受け取ることができなくなってしまいます。
なので、関数内でrandom()
やtime()
などを使っている場合は注意しましょう❗️
自分で実装する
cache
デコレータは、バージョン 3.9からしか使えないので自分で実装する方法を考えてみましょう。辞書を使うことで意外と簡単に実装することができます。
import time
# 実行時間を計測
def tictoc(func):
def _wrapper(*args, **keywargs):
start_time = time.time()
result = func(*args, **keywargs)
print('time: {:.9f} [sec]'.format(time.time() - start_time))
return result
return _wrapper
# キャッシュデコレータ
def cache(func):
c = {}
def _wrapper(*args):
if args not in c:
c[args] = func(*args)
return c[args]
return _wrapper
@tictoc
@cache
def func(num):
# なんらかの重い処理
for _ in range(1000000):
num += 1
return num
func(1)
func(1)
func(2)
func(2)
実行結果
time: 0.092554092 [sec]
time: 0.000005245 [sec]
time: 0.088145971 [sec]
time: 0.000007153 [sec]
辞書に引数の値のkeyが存在しなければ辞書[args] = func(*args)
を格納します。returnには、辞書[args]
で結果を返しています。
まとめ
この記事では、関数をキャッシュして高速化する方法を解説しました。
関数はキャッシュしておくことでめちゃくちゃ高速化することができます。少しだけ制限はありますが、よく使う関数や重い関数はキャッシュしておきましょう!
それでは今回の内容はここまでです。ではまたどこかで〜( ・∀・)ノ