0Pricing
Python Academy · レッスン

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      # recomputed

APIキャッシュとしての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フィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. itertools: 無限イテレーターと有限イテレーター
  2. itertools: 組合せ論
  3. functools: partial と reduce
  4. functools: lru_cache と cached_property
← Python Academyに戻る