class HashTable: def __init__(self, capacity=10): if type(capacity) is not int or isinstance(capacity, bool): raise TypeError() if capacity <= 0: raise ValueError() self._capacity = capacity self.empty = object() self.deleted = object() self.slots = [self.empty] * capacity self._size = 0 @property def capacity(self): return self._capacity def hash(self, key): if not isinstance(key, str): raise TypeError() h = 0 for ch in key: h = (31 * h + ord(ch)) % self._capacity return h def probe(self, key): if not isinstance(key, str): raise TypeError() start = self.hash(key) first_deleted = None for step in range(self._capacity): curr = (start + step) % self._capacity item = self.slots[curr] if item is self.empty: return first_deleted if first_deleted is not None else curr elif item is self.deleted: if first_deleted is None: first_deleted = curr elif item[0] == key: return curr if first_deleted is not None: return first_deleted raise RuntimeError() def load_factor(self): return self._size / self._capacity def __getitem__(self, key): try: pos = self.probe(key) except RuntimeError: raise KeyError(key) item = self.slots[pos] if isinstance(item, tuple) and item[0] == key: return item[1] raise KeyError(key) def __setitem__(self, key, value): try: pos = self.probe(key) if isinstance(self.slots[pos], tuple) and self.slots[pos][0] == key: self.slots[pos] = (key, value) return except RuntimeError: pass if (self._size + 1) / self._capacity > 0.7: old_slots = self.slots self._capacity *= 2 self.slots = [self.empty] * self._capacity self._size = 0 for item in old_slots: if isinstance(item, tuple): pos = self.probe(item[0]) self.slots[pos] = item self._size += 1 pos = self.probe(key) self.slots[pos] = (key, value) self._size += 1 def __delitem__(self, key): try: pos = self.probe(key) except RuntimeError: raise KeyError(key) item = self.slots[pos] if isinstance(item, tuple) and item[0] == key: self.slots[pos] = self.deleted self._size -= 1 return raise KeyError(key) def __contains__(self, key): try: pos = self.probe(key) except RuntimeError: return False item = self.slots[pos] return isinstance(item, tuple) and item[0] == key def __len__(self): return self._size def __iter__(self): for item in self.slots: if isinstance(item, tuple): yield item[0]