Categories: Python

【Python】関数の結果をキャッシュして高速化する

この記事では、関数をキャッシュして2度目以降の呼び出しを高速化する方法を解説します。

頻繁に呼び出す関数やコストのかかる関数などはキャッシュしておくことで高速化することができます。しかし、キャッシュに向かない関数もあるのでしっかり仕組みを理解して使いましょう。

それでは、関数のキャッシュ方法を見ていきましょう!

関数をキャッシュする方法

関数をキャッシュするには、Python 3.9で追加されたfunctoolsモジュールのcacheデコレータを使うことでめちゃくちゃ簡単に実装できます。

functoolsモジュールは標準ライブラリです。

それでは、実際にキャッシュした関数を使ってみましょう!

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 # ここからが本題 from functools import cache @tictoc @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として保存します。

そして、関数を呼び出した際に渡された引数が辞書のkeyとして存在する場合、関数を処理せずに辞書からkey(引数)に対応するvalue(戻り値)を返します。

注意として辞書を使用するので引数はハッシュ可能な値でなければならず、キーワード引数の順序が異なる場合は、異なるキャッシュとして保存されます。

例えば、f(a=1, b=2)f(b=2, a=1)は別。

あとで辞書を使ってキャッシュを実装しているので実際の仕組みはその時に確認してください。

キャッシュに向かない関数

同じ引数を指定しても同じ値を返さない関数をキャッシュしてしまうと、2回目以降に同じ値を返すようになってしまい、その関数の本来の結果を受け取ることができなくなってしまいます。

なので、関数内でrandom()time()などを使っている場合は注意しましょう❗️

from random import randint from functools import cache @cache def rand(): return randint(1, 10) print(rand()) print(rand()) print(rand()) print(rand())

実行結果

3 3 3 3

上記のように全て最初に返された結果になってしまう。

自分で実装する

cacheデコレータは、Python 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]で結果を返しています。

まとめ

この記事では、関数をキャッシュして高速化する方法を解説しました。

関数はキャッシュしておくことでめちゃくちゃ高速化することができます。少しだけ制限はありますが、よく使う関数や重い関数はキャッシュしておきましょう!

それでは今回の内容はここまでです。ではまたどこかで〜( ・∀・)ノ

  • functools --- 高階関数と呼び出し可能オブジェクトの操作 — Python ドキュメント
ゆうまる

独学でプログラミングを勉強しているおじさん。いろんな言語を勉強したが浅く広くなためあまり仕事につながらない。また忘れっぽいため自分のブログを備忘録としても使っている。産まれてこのかたずっとネコを飼ってる生粋のネコ派。最近お腹が出てきて筋トレに奮闘中!

Recent Posts

リッチなソースコードをブログに載っける方法

この記事では、ブログにソースコードを記述…

2か月 ago

【Mac】Homebrew自体のアンインストール

この記事では、パッケージ管理システムであ…

2か月 ago

【Python】MeCab(めかぶ)を使って形態素解析する

この記事では、Pythonで「MeCab…

2か月 ago

【Python】pytubeを使ってYouTubeの動画をダウンロードする

この記事ではpytubeを使ってYouT…

2か月 ago

【Python】Icrawlerを使って画像を大量にダウンロードする

この記事では、Icrawlerを使って画…

2か月 ago

【Python】Seleniumを使ってWebブラウザを自動化する

この記事では、Seleniumを使ってW…

2か月 ago