⚡ あなたのPythonコード、同じ計算を何度も繰り返していませんか?
再帰関数や繰り返し呼び出しが多いコードを書いていると、「動くけど遅い…」と感じる瞬間がきっと訪れます。実はその遅さ、たった1行のデコレーターを書き足すだけでウソみたいに解決することがあるんです。
キーワードは「メモ化(memoization)」。一度計算した結果を覚えておいて、同じ引数で呼ばれたら即座に返す——という仕組みです。マスターすれば、再帰アルゴリズムや動的計画法を使うコードが秒単位で高速化し、コードもスッキリ短くなります✨
📦 メモ化とは何か?仕組みをサクッと理解
メモ化とは、関数の呼び出し結果を保存しておき、同じ関数が同じ引数で再度呼ばれたときに保存しておいた結果を返すことで処理を高速化する手法のこと。
たとえば「引数に20を足して3で割って返す」という単純な関数 func1 があったとします。func1(110) を呼ぶと内部で計算して 40 が返ってきますが、メモ化しておけば「引数110のときの戻り値は40」を関数自身が覚えておいてくれます。次に同じ func1(110) が呼ばれたときには、中身の計算をスキップして記憶済みの40を即返す——それがメモ化です。
⚠️ 使う前に知っておきたい2つの注意点
- プロセスが終了するとデータは消える:メモ化はメモリ上で結果を保持するため、スクリプトを再実行すれば再びゼロから計算されます。「同じプロセスが起動中に同じ引数で呼ばれた場合に効く」と覚えましょう。
- 参照透過性のある関数だけに使う:同じ引数なら必ず同じ結果を返す関数(=参照透過性が保たれている関数)にだけ適用してください。関数の外のリストを書き換えるなど副作用がある関数にメモ化を入れると、思わぬバグの原因になります。
🚀 Pythonでのメモ化はたった1行で完了
Pythonでメモ化を実装するなら、標準ライブラリの functools モジュールが最強の味方です。書き方は驚くほど簡単。
Python 3.9以上:@cache が最速&シンプル
from functools import cache
@cache
def func1(x):
print(f"関数が呼ばれました 引数={x}")
return (x + 20) / 3
関数の上に @cache をつけるだけ。これだけで、同じ引数で2回目以降に呼ばれたときは中の処理が動かず、保存しておいた結果が即座に返ってきます。
Python 3.8以前:@lru_cache を使う
from functools import lru_cache
@lru_cache(maxsize=128)
def func1(x):
return (x + 20) / 3
lru_cache は記憶できる「引数と戻り値のペア」の上限を指定できるのが特徴。デフォルトは128個で、それ以上覚えたいなら maxsize=500 のように増やせます。一方、@cache はサイズ制限なしで動作も軽量・高速。3.9以上を使えるならこちらが第一候補です。
動作確認:呼ばれた回数を可視化
関数の先頭に print() を入れておくと、「2回目の func1(110) では print が出力されない=中身が実行されていない」ことが確認できます。それでも戻り値はちゃんと返ってくる——これがメモ化の威力です💪
🌀 真価を発揮するのは再帰アルゴリズム!フィボナッチで体感
単純な関数だとメモ化のありがたみは見えにくいもの。本領を発揮するのは再帰や動的計画法を使うアルゴリズムです。代表例として有名なフィボナッチ数列で速度差を見てみましょう。
素直に書いた再帰版(メモ化なし)
def fibonacci(i):
if i in (0, 1):
return i
return fibonacci(i - 1) + fibonacci(i - 2)
print(fibonacci(34)) # 5702887
とてもスッキリしたコードですが、これを fibonacci(34) で動かすと、内部では fibonacci(5) ですら何度も繰り返し呼ばれるなど、同じ計算が爆発的な回数行われます。実測すると約1秒前後かかることも。
@cache をつけるだけで爆速に
from functools import cache
@cache
def fibonacci(i):
if i in (0, 1):
return i
return fibonacci(i - 1) + fibonacci(i - 2)
print(fibonacci(34)) # 5702887
同じコードに @cache をつけるだけ。これで実行時間は 1秒 → 約 0.00005 秒(5.8e-05秒)レベル へ。一度計算した fibonacci(n) の結果を再利用できるため、無駄な再帰呼び出しが消滅し、計算量が劇的に減るからです。
time.perf_counter() を使って前後の差分を測ってみると、その差は誰の目にも明らか。再帰や動的計画法と相性抜群なテクニックだということが、肌で実感できるはずです🔥
📚 Pythonの「速いコード」を書く力を伸ばすおすすめアイテム
メモ化のような「知っているだけで世界が変わる」テクニックは、体系的に学ぶことで一気に身につきます。良書と快適な学習環境を揃えれば、コードを書くのが今よりずっと楽しくなりますよ。
📖 標準ライブラリを使い倒したい方へ
functools・itertools・pathlib といった「使うと差が出る」標準ライブラリの実例が豊富。本稿のメモ化と相性抜群の自動化スクリプトもたくさん学べます。
🐍 中級から上級への橋渡しに
「@functools.lru_cacheを使ってヘルパー関数の結果をメモ化する」など、本稿の延長線上のベストプラクティスが満載。Pythonらしい高速・堅牢なコードの書き方が90項目で身につきます。
🧮 アルゴリズム力を鍛える定番書
動的計画法や再帰のような、メモ化が真価を発揮する場面を体系的に学べる一冊。フィボナッチの先にある「実務で効く高速化テクニック」を身につけたい方におすすめです。
⌨️ 写経学習を快適にするキーボード
静かでスムーズな打鍵感は、長時間のコーディングに大きな差を生みます。サンプルコードを実際に打ち込んで挙動を確かめる学習スタイルとの相性が抜群です。
🖥️ 計測結果と公式ドキュメントを並べて読める外部モニター
左にエディタ、右にREPLや公式ドキュメントを並べておくと、メモ化の効果を測定するワークフローが格段にスムーズに。USB-C一本で接続できるのでデスクもスッキリです。
❓ よくある質問(FAQ)
🤔 @cache と @lru_cache、どちらを使うべき?
Python 3.9以上なら原則 @cache がおすすめです。サイズ制限がなく、実装も軽量で高速。一方、メモリ使用量を抑えたい場合や、覚えておくペアの数を制限したい場合は @lru_cache(maxsize=...) を選びましょう。
🧨 副作用がある関数にメモ化したらどうなる?
2回目以降は中身が実行されないため、外部のリスト更新やファイル書き込みなどの副作用が発生しなくなります。「呼び出したのに動いていない」というバグの温床になるので、副作用のある関数は絶対にメモ化しないでください。
📦 引数にリストや辞書を使う関数もメモ化できる?
そのままではできません。@lru_cache や @cache は引数を辞書のキーとして使うため、ハッシュ可能な型(int, str, tuple など)でなければエラーになります。リストはタプルに変換するなど工夫しましょう。
🔄 計算結果をクリアしたいときは?
func1.cache_clear() を呼ぶことで、保存されているキャッシュを丸ごと消せます。テスト時や、外部状態が変わったタイミングで便利です。
🌐 Webアプリのリクエストごとにメモ化は効く?
同じプロセスが生きている間は効きます。ただしプロセスが落ちると消えるので、より長く保持したい場合は Redis などの外部キャッシュを併用するのが定石です。
✨ まとめ:「同じ計算を二度するな」がPythonの合言葉
Pythonのメモ化は、再帰や動的計画法と組み合わせたときに本当の威力を発揮します。functools から cache をインポートし、関数の上に @cache をつけるだけ——たったそれだけで、フィボナッチ数列のような計算が1秒から1万分の1秒以下へと劇的に加速します。
もちろん、なんでもかんでもメモ化すればいいわけではありません。「参照透過性のある関数」「同じ引数で何度も呼ばれる処理」というポイントを押さえて使えば、副作用に悩まされず、コードもシンプルなまま速度を稼げます。
お気に入りの書籍と快適な作業環境を揃えて、Pythonの「知ってる人だけ得する」テクニックを一つずつ自分のものにしていきましょう。今日から、あなたのコードはもっと賢く、もっと速く動き出します🚀




































コメント