1
0
Fork 0
mirror of https://github.com/borgbackup/borg.git synced 2025-02-23 14:41:43 +00:00

lrucache - simpler _not_ to inherit from dict

We need to make sure dispose() is always called when necessary.
Using inheritance it's just too easy to forget a method,
that we needed to override.

I also find it confusing when an override method calls another
method, and you have yet to see whether the latter method is
overridden or not.  It didn't help that most of these methods
are actually operator overloads.

This turns out to require _less_ code :-).
(Admittedly the code could have been reduced a bit anyway
because python3's super() can be called without any arguments).
This commit is contained in:
Alan Jenkins 2015-08-12 23:30:26 +01:00
parent e3f671c4fb
commit 9ba7daa9c7
2 changed files with 19 additions and 29 deletions

View file

@ -1,20 +1,19 @@
class LRUCache(dict):
class LRUCache:
def __init__(self, capacity, dispose):
super(LRUCache, self).__init__()
self._cache = {}
self._lru = []
self._capacity = capacity
self._dispose = dispose
def __setitem__(self, key, value):
assert key not in self, (
assert key not in self._cache, (
"Unexpected attempt to replace a cached item."
" If this is intended, please delete or pop the old item first."
" The dispose function will be called on delete (but not pop).")
" If this is intended, please delete the old item first."
" The dispose function will be called on delete.")
self._lru.append(key)
while len(self._lru) > self._capacity:
del self[self._lru[0]]
return super(LRUCache, self).__setitem__(key, value)
self._cache[key] = value
def __getitem__(self, key):
try:
@ -22,7 +21,7 @@ def __getitem__(self, key):
self._lru.append(key)
except ValueError:
pass
return super(LRUCache, self).__getitem__(key)
return self._cache[key]
def __delitem__(self, key):
try:
@ -30,23 +29,22 @@ def __delitem__(self, key):
except ValueError:
pass
error = KeyError(key)
removed = super(LRUCache, self).pop(key, error)
removed = self._cache.pop(key, error)
if removed == error:
raise error
self._dispose(removed)
def pop(self, key, default=None):
try:
self._lru.remove(key)
except ValueError:
pass
return super(LRUCache, self).pop(key, default)
def __contains__(self, key):
return key in self._cache
def clear(self):
for value in self.values():
for value in self._cache.values():
self._dispose(value)
super(LRUCache, self).clear()
self._cache.clear()
def _not_implemented(self, *args, **kw):
raise NotImplementedError
popitem = setdefault = update = _not_implemented
# useful for testing
def items(self):
return self._cache.items()
def __len__(self):
return len(self._cache)

View file

@ -10,8 +10,7 @@ def test(self):
for i, x in enumerate('abc'):
c[x] = i
self.assert_equal(len(c), 2)
self.assert_equal(set(c), set(['b', 'c']))
self.assert_equal(set(c.items()), set([('b', 1), ('c', 2)]))
self.assert_equal(c.items(), set([('b', 1), ('c', 2)]))
self.assert_equal(False, 'a' in c)
self.assert_equal(True, 'b' in c)
self.assert_raises(KeyError, lambda: c['a'])
@ -25,10 +24,3 @@ def test(self):
self.assert_equal(len(c), 1)
self.assert_raises(KeyError, lambda: c['c'])
self.assert_equal(c['d'], 3)
def test_pop(self):
c = LRUCache(2, dispose=lambda _: None)
c[1] = 1
c[2] = 2
c.pop(1)
c[3] = 3