classDict(collections.MutableMapping): 'Space efficient dictionary with fast iteration and cheap resizes.'
@staticmethod def_gen_probes(hashvalue, mask): 'Same sequence of probes used in the current dictionary design' PERTURB_SHIFT = 5 if hashvalue < 0: hashvalue = -hashvalue i = hashvalue & mask yield i perturb = hashvalue whileTrue: i = (5 * i + perturb + 1) & 0xFFFFFFFFFFFFFFFF yield i & mask perturb >>= PERTURB_SHIFT
def_lookup(self, key, hashvalue): 'Same lookup logic as currently used in real dicts' assert self.filled < len(self.indices) # At least one open slot freeslot = None for i in self._gen_probes(hashvalue, len(self.indices) - 1): index = self.indices[i] if index == FREE: return (FREE, i) if freeslot isNoneelse (DUMMY, freeslot) elif index == DUMMY: if freeslot isNone: freeslot = i elif (self.keylist[index] is key or self.hashlist[index] == hashvalue and self.keylist[index] == key): return (index, i)
@staticmethod def_make_index(n): 'New sequence of indices using the smallest possible datatype' if n <= 2**7: return array.array('b', [FREE]) * n # signed char if n <= 2**15: return array.array('h', [FREE]) * n # signed short if n <= 2**31: return array.array('l', [FREE]) * n # signed long # python integers return [FREE] * n
def_resize(self, n): '''Reindex the existing hash/key/value entries. Entries do not get moved, they only get new indices. No calls are made to hash() or __eq__(). ''' n = 2 ** n.bit_length() # round-up to power-of-two self.indices = self._make_index(n) for index, hashvalue in enumerate(self.hashlist): for i in Dict._gen_probes(hashvalue, n - 1): if self.indices[i] == FREE: break self.indices[i] = index self.filled = self.used
def__contains__(self, key): index, i = self._lookup(key, hash(key)) return index >= 0
defget(self, key, default=None): index, i = self._lookup(key, hash(key)) return self.valuelist[index] if index >= 0else default
defpopitem(self): ifnot self.keylist: raise KeyError('popitem(): dictionary is empty') key = self.keylist[-1] value = self.valuelist[-1] del self[key] return key, value
defshow_structure(self): 'Diagnostic method. Not part of the API.' print('=' * 50) print(self) print('Indices:', self.indices) for i, row in enumerate( zip(self.hashlist, self.keylist, self.valuelist)): print(i, row) print('-' * 50)
if __name__ == '__main__': d = Dict([(a,a) for a in range(1)]) d.show_structure()