-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Expand file tree
/
Copy pathhashtable.py
More file actions
121 lines (97 loc) · 3.03 KB
/
hashtable.py
File metadata and controls
121 lines (97 loc) · 3.03 KB
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
# hashtable.py
from typing import Any, NamedTuple
DELETED = object()
class Pair(NamedTuple):
key: Any
value: Any
class HashTable:
@classmethod
def from_dict(cls, dictionary, capacity=None):
hash_table = cls(capacity or len(dictionary) * 10)
for key, value in dictionary.items():
hash_table[key] = value
return hash_table
def __init__(self, capacity):
if capacity < 1:
raise ValueError("Capacity must be a positive number")
self._slots = capacity * [None]
def __len__(self):
return len(self.pairs)
def __iter__(self):
yield from self.keys
def __delitem__(self, key):
for index, pair in self._probe(key):
if pair is None:
raise KeyError(key)
if pair is DELETED:
continue
if pair.key == key:
self._slots[index] = DELETED
break
else:
raise KeyError(key)
def __setitem__(self, key, value):
for index, pair in self._probe(key):
if pair is DELETED:
continue
if pair is None or pair.key == key:
self._slots[index] = Pair(key, value)
break
else:
raise MemoryError("Not enough capacity")
def __getitem__(self, key):
for _, pair in self._probe(key):
if pair is None:
raise KeyError(key)
if pair is DELETED:
continue
if pair.key == key:
return pair.value
raise KeyError(key)
def __contains__(self, key):
try:
self[key]
except KeyError:
return False
else:
return True
def __eq__(self, other):
if self is other:
return True
if type(self) is not type(other):
return False
return set(self.pairs) == set(other.pairs)
def __str__(self):
pairs = []
for key, value in self.pairs:
pairs.append(f"{key!r}: {value!r}")
return "{" + ", ".join(pairs) + "}"
def __repr__(self):
cls = self.__class__.__name__
return f"{cls}.from_dict({str(self)})"
def copy(self):
return HashTable.from_dict(dict(self.pairs), self.capacity)
def get(self, key, default=None):
try:
return self[key]
except KeyError:
return default
@property
def pairs(self):
return {pair for pair in self._slots if pair not in (None, DELETED)}
@property
def values(self):
return [pair.value for pair in self.pairs]
@property
def keys(self):
return {pair.key for pair in self.pairs}
@property
def capacity(self):
return len(self._slots)
def _index(self, key):
return hash(key) % self.capacity
def _probe(self, key):
index = self._index(key)
for _ in range(self.capacity):
yield index, self._slots[index]
index = (index + 1) % self.capacity