functools: lru_cache と cached_property
lru_cache と cached_property でコストの高い計算をキャッシュします。
「functools: lru_cache と cached_property」はCoddyKit上の無料Python Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPython Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Python Academyコースには全4レッスンが含まれています。
メモ化とは
メモ化とは、関数呼び出しの結果を引数に対応付けてキャッシュすることです。同じ引数で繰り返し呼び出すと、キャッシュされた結果がすぐに返されます。
def slow_fib(n):
if n < 2: return n
return slow_fib(n-1) + slow_fib(n-2)
# slow_fib(35) makes ~29 million calls
# With caching it makes only 35@lru_cache
@functools.lru_cache(maxsize=128)は、最近使われた結果をmaxsize件までキャッシュします。上限のないキャッシュにするにはmaxsize=Noneを設定します。
import functools
@functools.lru_cache(maxsize=None)
def fib(n):
if n < 2: return n
return fib(n-1) + fib(n-2)
print(fib(50)) # instant@cache — Python 3.9以降
functools.cacheはlru_cache(maxsize=None)の短縮形で、上限のないキャッシュをより簡潔な名前で利用できます。
import functools
@functools.cache
def factorial(n):
return n * factorial(n-1) if n else 1
print(factorial(10)) # 3628800キャッシュ情報の確認と消去
キャッシュされた関数は、.cache_info()(ヒット数、ミス数、サイズ)と.cache_clear()を提供します。
import functools
@functools.lru_cache(maxsize=100)
def square(n):
return n * n
for i in range(5): square(i % 3)
print(square.cache_info())
# CacheInfo(hits=2, misses=3, maxsize=100, currsize=3)
square.cache_clear()LRUの削除ポリシー
LRU(Least Recently Used)は、キャッシュがいっぱいになったとき、最も長い間アクセスされていない項目を削除します。
import functools
@functools.lru_cache(maxsize=3)
def compute(n):
print(f"computing {n}")
return n**2
for x in [1,2,3,4,1]: # 4 evicts 1 (LRU), then 1 re-computes
compute(x)ハッシュ可能な引数のみ
lru_cacheでは、すべての引数がハッシュ可能である必要があります。リストや辞書はハッシュ不可能なため、代わりにタプルを使ってください。
import functools
@functools.lru_cache(maxsize=None)
def sum_tuple(t): # tuple is hashable
return sum(t)
print(sum_tuple((1,2,3))) # 6
# sum_tuple([1,2,3]) # TypeError@cached_property
functools.cached_propertyはプロパティを1回だけ計算し、その結果をインスタンスにキャッシュして、ディスクリプターをその値で置き換えます。
import functools
class Circle:
def __init__(self, r):
self.r = r
@functools.cached_property
def area(self):
import math
print("computing...")
return math.pi * self.r ** 2
c = Circle(5)
print(c.area) # computing... 78.53...
print(c.area) # 78.53... (cached, no print)cached_propertyとpropertyの比較
@propertyはアクセスするたびに再計算します。@cached_propertyは1回だけ計算し、結果をinstance.__dict__に保存します。
import functools
class Expensive:
@property
def always(self): # runs every access
return sum(range(1_000_000))
@functools.cached_property
def once(self): # runs only first access
return sum(range(1_000_000))cached_propertyのスレッド安全性
cached_propertyはスレッドセーフではありません。複数のスレッドが同時にアクセスすると、計算が複数回実行される可能性があります。必要に応じてロックを使ってください。
import functools, threading
class SafeCache:
_lock = threading.Lock()
@functools.cached_property
def data(self):
with self._lock:
return expensive_computation()cached_propertyの無効化
インスタンス属性を削除するとキャッシュが無効になり、次回のアクセス時に再計算されます。
import functools
class Report:
@functools.cached_property
def summary(self):
return compute_summary()
r = Report()
_ = r.summary # computed
del r.summary # invalidate
_ = r.summary # recomputedAPIキャッシュとしてのlru_cacheの利用
不要なネットワーク呼び出しを避けるため、セッション中はAPIレスポンスをキャッシュします。最新のデータが必要になったらキャッシュを消去してください。
import functools, urllib.request, json
@functools.lru_cache(maxsize=32)
def get_user(user_id):
url = f"https://api.example.com/users/{user_id}"
with urllib.request.urlopen(url) as r:
return json.loads(r.read())
user = get_user(42) # network call
user = get_user(42) # cachedクイックチェック
@lru_cacheでデコレートした関数のキャッシュ済みの結果をすべて消去するメソッドはどれですか。
まとめ
@lru_cacheは引数(ハッシュ可能である必要があります)に対応付けて関数の結果をキャッシュします。@cacheは上限のないエイリアスです。@cached_propertyはインスタンスごとにプロパティの計算結果をキャッシュします。cache_info()で確認し、cache_clear()でリセットします。
よくある質問
「functools: lru_cache と cached_property」レッスンは無料ですか?
はい。「functools: lru_cache と cached_property」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Python Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Python Academyコースには全4レッスンが含まれています。
「functools: lru_cache と cached_property」で何を学びますか?
lru_cache と cached_property でコストの高い計算をキャッシュします。 ブラウザで直接実行するハンズオンコードでPython Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Python Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのPython Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。
「functools: lru_cache と cached_property」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このPython Academyレッスンでコードを書いて実行できますか?
はい。すべてのPython Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- itertools: 無限イテレーターと有限イテレーター
- itertools: 組合せ論
- functools: partial と reduce
- functools: lru_cache と cached_property