Proposed implementation for an Ordered Dictionary
Raymond Hettinger
python at rcn.com
Thu Feb 26 18:46:05 EST 2009
> > What about using a second dictionary (indexed by the incrementing
> > counter) instead of a list to record the insertion order? Then you
> > have O(1) deletion, and traversal takes an O(n log n) sorting
> > operation.
> My first attempt used exactly that approach and it works fine
> though it does complicate the code quite a bit and slows down
> all of the common operations by a constant factor.
FWIW, here is the code for that approach.
--------------------------------------
from collections import MutableMapping
class OrderedDict(dict):
def __init__(self, *args, **kwds):
if len(args) > 1:
raise TypeError('expected at 1 argument, got %d', len
(args))
self.clear()
self.update(*args, **kwds)
def clear(self):
self.__cached_sort = None
self.__cnt = 0
self.__order = {}
dict.clear(self)
def __setitem__(self, key, value):
if key not in self:
self.__cached_sort = None
self.__cnt += 1
self.__order[key] = self.__cnt
dict.__setitem__(self, key, value)
def __delitem__(self, key):
if key in self:
self.__cached_sort = None
del self.__order[key]
dict.__delitem__(self, key)
def __sorted(self):
# return keys in insertion order and cache the result
if self.__cached_sort is None:
self.__cached_sort = sorted(dict.keys(self),
key=self.__order.__getitem__)
return self.__cached_sort
def __iter__(self):
return iter(self.__sorted())
def __reversed__(self):
return iter(reversed(self.__sorted()))
def popitem(self):
# O(n) cost on the first pop and O(1) on successive pops
if not self:
raise KeyError
key = self.__sorted().pop()
del self.__order[key]
value = dict.pop(self, key)
return key, value
# Methods with indirect access via the above methods
setdefault = MutableMapping.setdefault
update = MutableMapping.update
pop = MutableMapping.pop
keys = MutableMapping.keys
values = MutableMapping.values
items = MutableMapping.items
def __repr__(self):
return '{' + ', '.join(map('%r: %r'.__mod__, self.items())) +
'}'
def copy(self):
return self.__class__(self)
@classmethod
def fromkeys(cls, iterable, value=None):
d = cls()
for key in iterable:
d[key] = value
return d
def __reduce__(self):
# pickle an items list and any extra info in the instance dict
items = list(self.items())
inst_dict = vars(self).copy()
for attr in vars(self.__class__):
inst_dict.pop(attr, None)
return (self.__class__, (items,), inst_dict)
More information about the Python-list
mailing list