patternpythonMinor
cache with least accessed items eviction
Viewed 0 times
evictionwithcacheitemsleastaccessed
Problem
I wrote this code, in order to implement a cache decorator which handles least accessed eviction.
The role of this decorator is to memoize decorated functions calls with args and kwargs and return the previously computed value if still in cache.
Warning, this handles only serializable args and kwargs, for the sake of simplicity.
Please review it and tell me if this makes sense.
The code:
```
import json
import hashlib
from collections import OrderedDict
class EvictionCache(OrderedDict):
def __init__(self, *args, **kwargs):
max_size = kwargs.pop('max_size', None)
super(EvictionCache, self).__init__(*args, **kwargs)
self._max_size = max_size
@property
def full(self):
return len(self) >= self._max_size
def set(self, hash, value):
if self.full:
self.evict()
super(EvictionCache, self).__setitem__(hash, value)
def get(self, hash):
value = super(EvictionCache, self).pop(hash, None)
if value:
super(EvictionCache, self).__setitem__(hash, value)
return value
def evict(self):
print 'evicting oldest cached item'
super(EvictionCache, self).popitem(last=False)
def cache(max_size=None):
def make_md5(args, kwargs):
data = dict(args=args, kwargs=kwargs)
md5 = hashlib.md5(json.dumps(data, sort_keys=True)).hexdigest()
return md5
def wrapper(f):
# make this cache belong to the decorated function to prevent the cache
# to be shared between different decorated functions
f._cache = EvictionCache(max_size=max_size)
def inner(*args, **kwargs):
md5 = make_md5(args, kwargs)
res = f._cache.get(md5)
if res:
print 'from cache'
return res
res = f(*args, **kwargs)
print 'to cache'
f._cache.set(md5, res)
return res
return inner
return wrapper
@cache(max_size=
The role of this decorator is to memoize decorated functions calls with args and kwargs and return the previously computed value if still in cache.
Warning, this handles only serializable args and kwargs, for the sake of simplicity.
Please review it and tell me if this makes sense.
The code:
```
import json
import hashlib
from collections import OrderedDict
class EvictionCache(OrderedDict):
def __init__(self, *args, **kwargs):
max_size = kwargs.pop('max_size', None)
super(EvictionCache, self).__init__(*args, **kwargs)
self._max_size = max_size
@property
def full(self):
return len(self) >= self._max_size
def set(self, hash, value):
if self.full:
self.evict()
super(EvictionCache, self).__setitem__(hash, value)
def get(self, hash):
value = super(EvictionCache, self).pop(hash, None)
if value:
super(EvictionCache, self).__setitem__(hash, value)
return value
def evict(self):
print 'evicting oldest cached item'
super(EvictionCache, self).popitem(last=False)
def cache(max_size=None):
def make_md5(args, kwargs):
data = dict(args=args, kwargs=kwargs)
md5 = hashlib.md5(json.dumps(data, sort_keys=True)).hexdigest()
return md5
def wrapper(f):
# make this cache belong to the decorated function to prevent the cache
# to be shared between different decorated functions
f._cache = EvictionCache(max_size=max_size)
def inner(*args, **kwargs):
md5 = make_md5(args, kwargs)
res = f._cache.get(md5)
if res:
print 'from cache'
return res
res = f(*args, **kwargs)
print 'to cache'
f._cache.set(md5, res)
return res
return inner
return wrapper
@cache(max_size=
Solution
I don't really feel subclassing a built-in data structure and defining your custom
Also check this implementation that has a simpler wrapper, but uses a "proxy" "cached function" helper class.
And, here is the source code of the Python's
Some other minor notes:
get() and set() methods is a good idea. One of the downsides of the current implementation is that if I would do self.cache[key] = value - this will not go through the cache's set() method and, hence, will not go through the "eviction" check at all. If you are subclassing a dictionary or an ordered dictionary, properly define the __getitem__() and __setitem__() magic methods instead of writing your custom get() and set() on top of it.Also check this implementation that has a simpler wrapper, but uses a "proxy" "cached function" helper class.
And, here is the source code of the Python's
functools.lru_cache for the reference.Some other minor notes:
- use
print()as a function for Python-3.x compatibility
Context
StackExchange Code Review Q#157939, answer score: 2
Revisions (0)
No revisions yet.