Issue 28014: Strange interaction between methods in subclass of C OrderedDict (original) (raw)
I'm not certain that the implementation of this subclass of OrderedDict is actually sane, but it works in 3.4 and fails in 3.5+.
The buggy implementation:
class SimpleLRUCache(OrderedDict):
def __init__(self, size):
super().__init__()
self.size = size
def __getitem__(self, item):
value = super().__getitem__(item)
self.move_to_end(item)
return value
def __setitem__(self, key, value):
while key not in self and len(self) >= self.size:
self.popitem(last=False)
super().__setitem__(key, value)
self.move_to_end(key)
When trying to add a new item after size
items are already in the cache, it will throw a KeyError with the key of the item in the oldest position. Something like:
s = SimpleLRUCache(2) s['t1'] = 1 s['t2'] = 2 s['t2'] # gives 2, properly moves 2 to the end s['t3'] = 3
Traceback (most recent call last): File "", line 1, in File "simple_lru.py", line 14, in setitem self.popitem(last=False) File "simple_lru.py", line 9, in getitem self.move_to_end(item) KeyError: 't3'
I can work around the failure by implementing getitem as follows:
def __getitem__(self, item):
value = super().__getitem__(item)
del self[item]
self[item] = value
return value
Attached is a script with a couple of tests that pass with 3.4 and fail with 3.5+.
Note: This class doesn't actually work on 3.4 in other ways (because getitem is not idempotent, while OrderedDict assumes it is):
s = SimpleLRUCache(2) s['t1'] = 1 s SimpleLRUCache([('t1', 1)]) s['t2'] = 2 s SimpleLRUCache([('t1', 1)]) s SimpleLRUCache([('t2', 2)]) # <-- No changes, repr different
If your getitem isn't idempotent, you've broken a basic assumption built into the logic of the other methods you inherited, and you're going to need to override other methods to avoid misbehavior.