1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
|
"""Provides a dictionary that is indexed by insertion order."""
__author__ = "Niklas Fiekas"
__email__ = "niklas.fiekas@backscattering.de"
__version__ = "1.3.0"
__license__ = "PSFL"
import collections
import collections.abc
import operator
import reprlib
class IndexedOrderedDict(dict):
"""A dictionary that is indexed by insertion order."""
def __init__(self, *args, **kwds):
"""
Initialize an ordered dictionary. The signature is the same as
regular dictionaries. Keyword argument order is preserved.
"""
if len(args) > 1:
raise TypeError('expected at most 1 arguments, got %d' % len(args))
self._map = []
self.__update(*args, **kwds)
def __setitem__(self, key, value, *, __dict_setitem=dict.__setitem__):
"""iod.__setitem__(i, y) <==> iod[i] = y"""
if key not in self:
self._map.append(key)
__dict_setitem(self, key, value)
def __delitem__(self, key, *, __dict_delitem=dict.__delitem__):
"""iod.__delitem__(y) <==> del iod[y]"""
__dict_delitem(self, key)
self._map.remove(key)
def __iter__(self):
"""iod.__iter__() <==> iter(iod)"""
return self._map.__iter__()
def __reversed__(self):
"""iod.__reversed__() <==> reversed(iod)"""
return self._map.__reversed__()
def clear(self):
"""iod.clear() -> None. Remove all items from iod."""
self._map.clear()
dict.clear(self)
def popitem(self, last=True):
"""
iod.popitem() -> (k, v), return and remove a (key, value) pair.
Pairs are returned LIFO order if last is true or FIFI order if false.
"""
key = self._map.pop() if last else self._map.pop(0)
value = dict.pop(self, key)
return key, value
def move_to_end(self, key, last=True):
"""
Move an existing element to the end (or beginning if last==False).
Raises KeyError if the element does not exist.
When last=True, acts like a faster version of self[key]=self.pop(key).
"""
self._map.remove(key)
if last:
self._map.append(key)
else:
self._map.insert(0, key)
update = __update = collections.abc.MutableMapping.update
__ne__ = collections.abc.MutableMapping.__ne__
def keys(self):
return IndexedKeysView(self)
def values(self):
return IndexedValuesView(self)
def items(self):
return IndexedItemsView(self)
__marker = object()
def pop(self, key, default=__marker):
"""
iod.pop(k[,d]) -> v, remove specified key and return the corresponding
value. If key is not found, d is returned if given, otherwise KeyError
is raised.
"""
if key in self:
result = self[key]
del self[key]
return result
if default is self.__marker:
raise KeyError(key)
return default
def setdefault(self, key, default=None):
"""
iod.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in d
"""
if key in self:
return self[key]
self[key] = default
return default
def sort(self, *, key=None, reverse=False):
"""Sort the dictionary by key in place."""
self._map.sort(key=key, reverse=reverse)
@reprlib.recursive_repr()
def __repr__(self):
"""iod.__repr__() <==> repr(iod)"""
if not self:
return '%s()' % (self.__class__.__name__, )
return '%s(%r)' % (self.__class__.__name__, list(self.items()))
def __reduce__(self):
"""Return state information for pickling"""
inst_dict = vars(self).copy()
for k in vars(IndexedOrderedDict()):
inst_dict.pop(k, None)
return self.__class__, (), inst_dict or None, None, iter(self.items())
def copy(self):
"""od.copy() -> a shallow copy of iod"""
return self.__class__(self)
@classmethod
def fromkeys(cls, iterable, value=None):
"""
IOD.fromkeys(S[,v]) -> New indexed ordered dictionary with keys from S.
If not specified, the value defaults to None.
"""
self = cls()
for key in iterable:
self[key] = value
return self
def __eq__(self, other):
"""
iod.__eq__(y) <==> iod==y. Comparison to another IOD is
order-sensitive while comparison to a regular mapping is
order-insensitive.
"""
if isinstance(other, collections.OrderedDict) or isinstance(other, IndexedOrderedDict):
return dict.__eq__(self, other) and all(map(operator.eq, self, other))
return dict.__eq__(self, other)
def __or__(self, other):
if not isinstance(other, dict):
return NotImplemented
new = self.__class__(self)
new.update(other)
return new
def __ror__(self, other):
if not isinstance(other, dict):
return NotImplemented
new = self.__class__(other)
new.update(self)
return new
def __ior__(self, other):
self.update(other)
return self
Dict = IndexedOrderedDict
class IndexedKeysView(collections.abc.KeysView):
def __getitem__(self, index):
return self._mapping._map[index]
def index(self, x):
return self._mapping._map.index(x)
class IndexedValuesView(collections.abc.ValuesView):
def __getitem__(self, index):
key = self._mapping._map[index]
return self._mapping[key]
class IndexedItemsView(collections.abc.ItemsView):
def __getitem__(self, index):
key = self._mapping._map[index]
return key, self._mapping[key]
|