Browse Source

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).
Alan Jenkins 10 years ago
parent
commit
9ba7daa9c7
2 changed files with 19 additions and 29 deletions
  1. 18 20
      attic/lrucache.py
  2. 1 9
      attic/testsuite/lrucache.py

+ 18 - 20
attic/lrucache.py

@@ -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 @@ class LRUCache(dict):
             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 @@ class LRUCache(dict):
         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()
+
+    # useful for testing
+    def items(self):
+        return self._cache.items()
 
-    def _not_implemented(self, *args, **kw):
-        raise NotImplementedError
-    popitem = setdefault = update = _not_implemented
+    def __len__(self):
+        return len(self._cache)

+ 1 - 9
attic/testsuite/lrucache.py

@@ -10,8 +10,7 @@ class LRUCacheTestCase(AtticTestCase):
         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 @@ class LRUCacheTestCase(AtticTestCase):
         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