AIニュース最前線
最新ニュースAI日報Hacker日報週報動画AIツールトレンド企業

AIニュース最前線

世界中のAI最新情報を日本語で毎時更新

最新ニュース日報トレンド企業プレミアムRSS
© 2026 ainew.jp特定商取引法に基づく表記
ニュース一覧元記事を開く
Answer.AI·2025年6月7日 09:00·約8分で読める

Flexicacheの探求

#Python#オープンソース#パフォーマンス最適化#fastcore
TL;DR

Answer.AI の記事は、Python パッケージ fastcore に追加された新しいキャッシュデコレータ「flexicache」の機能と実装例を解説している。

AI深層分析2026年5月3日 01:15
3
注目/ 5段階
深度40%
4
関連度30%
2
実用性20%
5
革新性10%
3

キーポイント

1

flexicache の概要と目的

fastcore ライブラリに組み込まれた flexicache は、関数やメソッドの結果を柔軟にキャッシュするためのデコレータであり、メモリ内キャッシングの効率化を目的としている。

2

時間ベースとファイル変更ベースの無効化ポリシー

time_policy を使用して指定秒数でキャッシュを期限切れにし、mtime_policy を使用して対象ファイルが更新された瞬間に自動的にキャッシュを無効化する機能を提供する。

3

実装とテストの具体例

Python の標準ライブラリや pathlib を用いたコードスニペットを通じて、キャッシュの有効期間やファイルタッチによる無効化動作を実証する具体的な使用方法が示されている。

4

複数のキャッシュポリシーの併用

flexicache は time_policy と mtime_policy を同時に使用でき、時間制限到達またはファイル更新のいずれかが発生するとキャッシュが無効化されます。

5

LRU キャッシング機能の実装

maxsize パラメータを指定することで LRU(Least Recently Used)戦略を実現し、最大サイズを超えると最も古いエントリから削除します。

6

LRU キャッシュの動作確認

キャッシュサイズに達すると、最も古く使用されたアイテム(Least Recently Used)が自動的に削除され、新しいアイテムが追加されることを示しています。

7

timed_cache の機能

fastcore.xtras.timed_cache は functools.lru_cache にタイムアウト機能を追加したラッパーであり、指定時間経過後にキャッシュを無効化します。

影響分析・編集コメントを表示

影響分析

このツールは、Python 開発者にとって関数の実行結果を効率的に管理し、I/O 操作や計算コストの高い処理のパフォーマンスを向上させるための強力な手段を提供します。特にファイルの状態に応じた動的キャッシュ制御が可能になることで、データ整合性を保ちつつ速度を最大化するアプリケーション開発の幅を広げます。

編集コメント

Jeremy Howard 氏による解説は、開発者コミュニティにとって即座に活用できる実用的な知見を含んでおり、コードの保守性と実行効率を両立させるための具体的な解決策を示しています。

ジェレミーからのメッセージ:伝説的なダニエル・ロイ・グリーンフェルドが、私が fastcore に最近追加した flexicache という非常に新しい機能に時間を割いて詳しく調査してくれたことを嬉しく思います。これは今では私が常に使用している非常に便利な小さなツールです。ダンと私が気に入っているのと同じくらい、あなたも気に入ってくれることを願っています!

Python でコーディングする際、関数やメソッドの結果をメモリーにキャッシュしたり、時には memcached のような一時的なストレージにキャッシュしたりするためにデコレーターをよく使用します。実際、私は複数のキャッシュデコレーターを開発・作成しており、その中には Python 3.8 の @cached_property デコーターの実装に影響を与えたものも含まれています。

flexicache という名前のキャッシュデコーターは fastcore ライブラリの一部です。flexicache を使用すると、関数やメソッドの結果を柔軟にメモリー内にキャッシュすることができます。LRU キャッシング(Least Recently Used caching)の実装を持つだけでなく、デコーターの各使用において、1 つ以上のキャッシュ無効化ポリシーを使用するように構成できます。

time_policy と mtime_policy という 2 つのポリシーが、それぞれ時間とファイルの変更時刻に基づいてキャッシュを無効化するために使用されます。time_policy は指定された秒数後にキャッシュを無効化し、mtime_policy は最後にキャッシュされた時点以降にファイルが変更されている場合にキャッシュを無効化します。

試してみましょう!

基本的な使用方法

必要なライブラリをインポートする

from fastcore.xtras import flexicache, time_policy, mtime_policy

キャッシュの有効性とキャッシュの無効化を検証するために使用されるライブラリ

from random import randint

from pathlib import Path

from time import sleep

ここでは、1 から 1000 の間の数を返す単純な関数を紹介し、これがキャッシュされている様子を示します。この関数はすべての例で使用します。

def random_func(v):

return randint(1, 1000)

関数がキャッシュされていないため False をアサート

assert random_func(1) != random_func(1)

時間ポリシー (time_policy)

これが、time_policy を使用して関数をキャッシュする方法です。

@flexicache(time_policy(.1))

def random_func():

return randint(1, 1000)

関数がキャッシュされているため True をアサート

assert random_func() == random_func()

random_func の呼び出し間の時間をシミュレートするために、sleep 関数を使用しましょう。

result = random_func()

関数がキャッシュされているため True

assert result == random_func()

キャッシュが期限切れになるように .2 秒間待機

sleep(0.2)

キャッシュが期限切れになり、関数が再度呼び出されるため False をアサート

assert result != random_func()

ファイル更新時刻ポリシー (mtime_policy)

mtime_policy で試してみましょう。ファイルにタッチすることでキャッシュが無効化されるかを確認します。ここでは、このサイトの main.py ファイルをタッチする対象ファイルとして使用します。

@flexicache(mtime_policy('../../main.py'))

def random_func():

return randint(1, 1000)

関数がキャッシュされているため True をアサート

assert random_func() == random_func()

次に、Path.touch() メソッドを使用してファイルにタッチします。これにより、ファイルの更新時刻が現在の時刻に更新され、キャッシュが無効化されるはずです。

キャッシュ結果を呼び出す関数を実行

result = random_func()

assert result == random_func() # True、関数がキャッシュされているため

ファイルの更新時刻を変更し、キャッシュを無効化

Path('../../main.py').touch()

キャッシュが無効化されたため False をアサート

assert result != random_func()

複数のポリシーの使用

flexicache のユニークな機能は、同時に複数のポリシーを使用できる点です。これにより、異なるキャッシュ戦略の利点を組み合わせることが可能になります。この例では、time_policy と mtime_policy の両方を併用します。つまり、時間制限に達した場合またはファイルが変更された場合のいずれかでキャッシュが無効化されます。

両方のポリシーでキャッシュをテストする方法は、以前の例と同じです。まず time ポリシーで関数を呼び出し、次に mtime ポリシーで呼び出し、最後に両方のポリシーを組み合わせて呼び出します。また、キャッシュが無効化されるかどうかを確認するためにファイルにも触れます。

@flexicache(time_policy(.1), mtime_policy('../../main.py'))

def random_func():

return randint(1, 1000)

True、関数がキャッシュされているため

assert random_func() == random_func()

時間による無効化のテストは以前と同じです。関数を呼び出し、時間制限に達するまで待ち、その後再度呼び出してキャッシュが無効化されるかどうかを確認します。

result = random_func()

True as the function is cached

assert result == random_func()

Sleep for .2 seconds to allow cache to expire

sleep(0.2)

False as the cache has expired and the function is called again

assert result != random_func()

テストファイルのタイムスタンプが以前と同じであることを確認します。関数を呼び出してファイルをタッチし、再度呼び出すことでキャッシュが無効化されるかを確認します。

結果をキャッシュするために関数を呼び出す

result = random_func()

True as the function is cached

assert result == random_func()

ファイルの更新時刻を更新してキャッシュを無効化する

Path('../../main.py').touch()

キャッシュが無効化されたことを確認(False を期待)

assert result != random_func()

LRU キャッシングはどうでしょうか?

次に、flexicache デコレータが LRU キャッシングの代替としてどのように動作するかをテストしてみましょう。参考までに、LRU キャッシング (Least Recently Used caching) は、最も最近使用されたアイテムを追跡し、キャッシュが最大サイズに達したときに最も古く使用されたアイテムを削除するキャッシング戦略です。つまり、スペースがなくなると、最初に最新アイテムをキャッシュから取り出します。これは FIFO (First In, First Out) 戦略を使用して、キャッシュから最も古いアイテムを削除します。

flexicache を maxsize(キャッシュの最大サイズ)2 で使用します。つまり、2 回保存した後、最も古いキャッシュエントリが破棄され始めます。キャッシュ関数のエントリは引数 (v) によって識別されるため、関数に引数を追加します。

@flexicache(maxsize=2)

def random_func(v):

return randint(1, 1000)

仕組みを見てみましょう。

result1 = random_func(1)

関数がキャッシュされているため True

assert result1 == random_func(1)

関数がキャッシュされているため True

assert random_func(2) == random_func(2)

So far so good. The cache is working as expected. Now let's start evicting the first items added to the cache. We'll add a third item to the cache and see if the first one is evicted.

3 の関数がキャッシュされているため True、

ただし random_func2(1) の結果は除外されます

assert random_func(3) == random_func(3)

最初の結果がもはやキャッシュされていないため False

assert result1 != random_func(1)

timed_cache convenience wrapper

lru_cache は Python に組み込まれたデコレータで、関数の結果をキャッシュするためのシンプルな方法を提供します。これは Least Recently Used (LRU) キャッシング戦略を使用しており、引数に基づいて最も最近使用された項目を追跡し、キャッシュが最大サイズに達したときに最も最近使用されていない項目を削除します。つまり、スペースがなくなると、最新の項目からキャッシュから取り出します。

欠点はタイムアウト機能がないことです。特定の時間だけ結果をキャッシュしたい場合は、それを自分で実装する必要があります。

fastcore.xtras.timed_cache は、functools.lru_cache にタイムアウト機能を追加した flexicache の実装です。

from fastcore.xtras import timed_cache

@flexicache(time_policy(.1), maxsize=2) のショートカット

@timed_cache(.1, maxsize=2)

def random_func(v):

return randint(1, 1000)

関数がキャッシュされている場合も真となる

assert random_func(1) == random_func(1)

time_policy(.1)、maxsize=2 を指定した flexicache で、以前と同じようにタイムアウトのテストを行います。関数を呼び出し、タイムアウトに達するまで待機し、その後再度呼び出してキャッシュが無効化されたかを確認します。

キャッシュが期限切れになるのに十分な時間待つ

sleep(0.2)

キャッシュが時間無効化されるため偽となる

assert result1 != random_func(1)

最後に、LRU(Least Recently Used)キャッシュが最初にキャッシュされたアイテムを削除していることを確認します。これは上記の LRU キャッシングに関するセクションで用いたのと同じ LRU キャッシュセットのテストです。再びキャッシュに第 3 のアイテムを追加し、最初のアイテムが除外されるかを確認します。

result1 = random_func(1)

関数がキャッシュされているため真となる

assert result1 == random_func(1)

関数がキャッシュされているため真となる

assert random_func(2) == random_func(2)

3 のための関数がキャッシュされるが、random_func2(1) の結果を除外する

assert random_func(3) == random_func(3)

最初の結果はもはやキャッシュされていないため偽となる

assert result1 != random_func(1)

image
image

images/exploring-flexicache.png

原文を表示

Note from Jeremy: I’m thrilled that the legendary Daniel Roy Greenfeld took the time to dig into a very recent addition I made to fastcore: flexicache. It’s a super useful little tool which nowadays I use all the time. I hope you like it as much as Danny and I do!

When coding in Python really like to use decorators to cache results from functions and methods, often to memory and sometimes to ephemeral stores like memcached. In fact, I’ve worked on and created several cache decorators, including one that influenced the implementation of the @cached_property decorator in Python 3.8.

A cache decorator called flexicache is part of the fastcore library. flexicache allows you to cache in memory results from functions and methods in a flexible way. Besides having an implementation of LRU caching, each use of the decorator can be configured to use one or more cache invalidation policies.

Two policies, time_policy and mtime_policy are used to invalidate the cache based on time and file modification time respectively. The time_policy invalidates the cache after a specified number of seconds, while the mtime_policy invalidates the cache if the file has been modified since the last time it was cached.

Let’s try it out!

Basic usage

Import necessary libraries

from fastcore.xtras import flexicache, time_policy, mtime_policy

Libraries used in testing cache validity and cache invalidation

from random import randint

from pathlib import Path

from time import sleep

Here’s a simple function returning a number between 1 to 1000 that we can show being cached. We’ll use this in all our examples.

def random_func(v):

return randint(1, 1000)

Assert False as the function is not cached

assert random_func(1) != random_func(1)

Time policy

This is how we use the time_policy to cache the function.

@flexicache(time_policy(.1))

def random_func():

return randint(1, 1000)

assert True as the function is cached

assert random_func() == random_func()

Let’s use the sleep function to simulate time between calls to random_func.

result = random_func()

True as the function is cached

assert result == random_func()

Sleep for .2 seconds to allow cache to expire

sleep(0.2)

Assert False as the cache has expired and the function is called again

assert result != random_func()

File modification time (mtime_policy)

We’ll try with mtime_policy, checking to see if touching a file invalidates the cache. We’ll use this site’s main.py file as the file to touch.

@flexicache(mtime_policy('../../main.py'))

def random_func():

return randint(1, 1000)

Assert True as the function is cached

assert random_func() == random_func()

Now let’s use the Path.touch() method to touch the file. This will update the file’s modification time to the current time, which should invalidate the cache.

Call the function to cache the result

result = random_func()

assert result == random_func() # True as the function is cached

Update the file's modification time, which invalidates the cache

Path('../../main.py').touch()

Assert False as the cache is invalidated

assert result != random_func()

Using multiple policies

A unique feature of flexicache is that you can use multiple policies at the same time. This allows you to combine the benefits of different caching strategies. In this example, we’ll use both time_policy and mtime_policy together. This means that the cache will be invalidated if either the time limit is reached or the file has been modified.

Testing the cache with both policies is identical to the previous examples. We’ll call the function, first with the time policy, then with the mtime policy, and finally with both policies. We’ll also touch the file to see if it invalidates the cache.

@flexicache(time_policy(.1), mtime_policy('../../main.py'))

def random_func():

return randint(1, 1000)

True as the function is cached

assert random_func() == random_func()

Testing time invalidation is the same as before. We’ll call the function, wait for the time limit to be reached, and then call it again to see if the cache is invalidated.

result = random_func()

True as the function is cached

assert result == random_func()

Sleep for .2 seconds to allow cache to expire

sleep(0.2)

False as the cache has expired and the function is called again

assert result != random_func()

Testing file timestamp is the same as before. We’ll call the function, touch the file, and then call it again to see if the cache is invalidated.

Call the function to cache the result

result = random_func()

True as the function is cached

assert result == random_func()

Update the file's modification time, which invalidates the cache

Path('../../main.py').touch()

Assert False as the cache is invalidated

assert result != random_func()

What about LRU caching?

Now let’s test out the flexicache decorator to see how it behaves as an lru_cache replacement. For reference, LRU caching is a caching strategy that keeps track of the most recently used items and removes the least recently used items when the cache reaches its maximum size. In other words, it takes out the latest items from the cache first when it runs out of space. It uses the FIFO (first in, first out) strategy to remove the oldest items from the cache.

We’ll use flexicache with maxsize (of cache) of 2, meaning after 2 saves it starts discarding the oldest cache entries. Entries in cache functions are identified in the cache by arguments (v),so we add an argument to the function.

@flexicache(maxsize=2)

def random_func(v):

return randint(1, 1000)

Let’s see how it works.

result1 = random_func(1)

True as the function is cached

assert result1 == random_func(1)

True as the function is cached

assert random_func(2) == random_func(2)

So far so good. The cache is working as expected. Now let’s start evicting the first items added to the cache. We’ll add a third item to the cache and see if the first one is evicted.

True as the function for 3 is cached,

but it will evict the result of random_func2(1)

assert random_func(3) == random_func(3)

False as the first result is no longer cached

assert result1 != random_func(1)

timed_cache convenience wrapper

lru_cache is a built-in Python decorator that provides a simple way to cache the results of a function. It uses a Least Recently Used (LRU) caching strategy, which means that it keeps track of the most recently used items as based on arguments and removes the least recently used items when the cache reaches its maximum size. In other words, it takes out the latest items from the cache first when it runs out of space.

The downside is that it doesn’t have a timeout feature, so if you want to cache results for a specific amount of time, you need to implement that yourself.

fastcore.xtras.timed_cache is an implementation of flexicache that adds a timeout feature to functools.lru_cache.

from fastcore.xtras import timed_cache

shortcut for @flexicache(time_policy(.1), maxsize=2)

@timed_cache(.1, maxsize=2)

def random_func(v):

return randint(1, 1000)

True as the function is cached

assert random_func(1) == random_func(1)

Testing the timeout is the same as before with flexicache(time_policy(.1), maxsize=2). We’ll call the function, wait for the timeout to be reached, and then call it again to see if the cache is invalidated.

Wait long enough for the cache to expire

sleep(0.2)

Assert False as the cache is time invalidated

assert result1 != random_func(1)

Finally, confirm that the LRU cache is removing the first cached item. This is the same LRU cache set of tests we used in the section above about LRU caching. Again, we’ll add a third item to the cache and see if the first one is evicted.

result1 = random_func(1)

True as the function is cached

assert result1 == random_func(1)

True as the function is cached

assert random_func(2) == random_func(2)

True as the function for 3 is cached,

but it will evict the result of random_func2(1)

assert random_func(3) == random_func(3)

False as the first result is no longer cached

assert result1 != random_func(1)

image
image

images/exploring-flexicache.png

この記事をシェア

関連記事

KDnuggets★42026年6月1日 21:00

データサイエンティストが知るべき Python の必須概念 5 つ

この記事は、データサイエンティストがスパゲッティコードから高速で生産レベルのデータパイプラインへ移行するために必要な Python の 5 つの必須概念を詳しく解説しています。

KDnuggets★32026年6月10日 21:00

退屈な PDF タスクを自動化する Python スクリプト 5 つ

KDnuggets は、PDF の処理や変換など日常的な作業を自動化するための有用な Python スクリプト 5 つを紹介した。

GitHub Blog2026年6月9日 01:00

GitHub for Beginners:よくある質問への回答

GitHub は初心者向けシリーズの最終回で、開発を始めたばかりの人々が抱える一般的な疑問に答えた。

今日のまとめ

AI日報で今日の重要ニュースをまとめ読み

ニュース一覧に戻る元記事を読む