Skip to content

Commit 35da9ed

Browse files
authored
Merge pull request #239 from realpython/hashtable
Materials supplementing the hash table tutorial
2 parents dd19b95 + dca7513 commit 35da9ed

File tree

37 files changed

+5213
-0
lines changed

37 files changed

+5213
-0
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# hashtable.py
2+
3+
4+
class HashTable:
5+
def __init__(self, capacity):
6+
self.values = capacity * [None]
7+
8+
def __len__(self):
9+
return len(self.values)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# test_hashtable.py
2+
3+
from hashtable import HashTable
4+
5+
6+
def test_should_create_hashtable():
7+
assert HashTable(capacity=100) is not None
8+
9+
10+
def test_should_report_capacity():
11+
assert len(HashTable(capacity=100)) == 100
12+
13+
14+
def test_should_create_empty_value_slots():
15+
assert HashTable(capacity=3).values == [None, None, None]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# hashtable.py
2+
3+
BLANK = object()
4+
5+
6+
class HashTable:
7+
def __init__(self, capacity):
8+
self.values = capacity * [BLANK]
9+
10+
def __len__(self):
11+
return len(self.values)
12+
13+
def __setitem__(self, key, value):
14+
index = hash(key) % len(self)
15+
self.values[index] = value
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# test_hashtable.py
2+
3+
from hashtable import HashTable, BLANK
4+
5+
6+
def test_should_create_hashtable():
7+
assert HashTable(capacity=100) is not None
8+
9+
10+
def test_should_report_capacity():
11+
assert len(HashTable(capacity=100)) == 100
12+
13+
14+
def test_should_create_empty_value_slots():
15+
assert HashTable(capacity=3).values == [BLANK, BLANK, BLANK]
16+
17+
18+
def test_should_insert_key_value_pairs():
19+
hash_table = HashTable(capacity=100)
20+
21+
hash_table["hola"] = "hello"
22+
hash_table[98.6] = 37
23+
hash_table[False] = True
24+
25+
assert "hello" in hash_table.values
26+
assert 37 in hash_table.values
27+
assert True in hash_table.values
28+
29+
assert len(hash_table) == 100
30+
31+
32+
def test_should_not_contain_none_value_when_created():
33+
assert None not in HashTable(capacity=100).values
34+
35+
36+
def test_should_insert_none_value():
37+
hash_table = HashTable(capacity=100)
38+
hash_table["key"] = None
39+
assert None in hash_table.values
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# hashtable.py
2+
3+
BLANK = object()
4+
5+
6+
class HashTable:
7+
def __init__(self, capacity):
8+
self.values = capacity * [BLANK]
9+
10+
def __len__(self):
11+
return len(self.values)
12+
13+
def __setitem__(self, key, value):
14+
index = hash(key) % len(self)
15+
self.values[index] = value
16+
17+
def __getitem__(self, key):
18+
index = hash(key) % len(self)
19+
value = self.values[index]
20+
if value is BLANK:
21+
raise KeyError(key)
22+
return value
23+
24+
def __contains__(self, key):
25+
try:
26+
self[key]
27+
except KeyError:
28+
return False
29+
else:
30+
return True
31+
32+
def get(self, key, default=None):
33+
try:
34+
return self[key]
35+
except KeyError:
36+
return default
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# test_hashtable.py
2+
3+
import pytest
4+
5+
from hashtable import HashTable, BLANK
6+
7+
8+
@pytest.fixture
9+
def hash_table():
10+
sample_data = HashTable(capacity=100)
11+
sample_data["hola"] = "hello"
12+
sample_data[98.6] = 37
13+
sample_data[False] = True
14+
return sample_data
15+
16+
17+
def test_should_create_hashtable():
18+
assert HashTable(capacity=100) is not None
19+
20+
21+
def test_should_report_capacity():
22+
assert len(HashTable(capacity=100)) == 100
23+
24+
25+
def test_should_create_empty_value_slots():
26+
assert HashTable(capacity=3).values == [BLANK, BLANK, BLANK]
27+
28+
29+
def test_should_insert_key_value_pairs():
30+
hash_table = HashTable(capacity=100)
31+
32+
hash_table["hola"] = "hello"
33+
hash_table[98.6] = 37
34+
hash_table[False] = True
35+
36+
assert "hello" in hash_table.values
37+
assert 37 in hash_table.values
38+
assert True in hash_table.values
39+
40+
assert len(hash_table) == 100
41+
42+
43+
def test_should_not_contain_none_value_when_created():
44+
assert None not in HashTable(capacity=100).values
45+
46+
47+
def test_should_insert_none_value():
48+
hash_table = HashTable(capacity=100)
49+
hash_table["key"] = None
50+
assert None in hash_table.values
51+
52+
53+
def test_should_find_value_by_key(hash_table):
54+
assert hash_table["hola"] == "hello"
55+
assert hash_table[98.6] == 37
56+
assert hash_table[False] is True
57+
58+
59+
def test_should_raise_error_on_missing_key():
60+
hash_table = HashTable(capacity=100)
61+
with pytest.raises(KeyError) as exception_info:
62+
hash_table["missing_key"]
63+
assert exception_info.value.args[0] == "missing_key"
64+
65+
66+
def test_should_find_key(hash_table):
67+
assert "hola" in hash_table
68+
69+
70+
def test_should_not_find_key(hash_table):
71+
assert "missing_key" not in hash_table
72+
73+
74+
def test_should_get_value(hash_table):
75+
assert hash_table.get("hola") == "hello"
76+
77+
78+
def test_should_get_none_when_missing_key(hash_table):
79+
assert hash_table.get("missing_key") is None
80+
81+
82+
def test_should_get_default_value_when_missing_key(hash_table):
83+
assert hash_table.get("missing_key", "default") == "default"
84+
85+
86+
def test_should_get_value_with_default(hash_table):
87+
assert hash_table.get("hola", "default") == "hello"
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# hashtable.py
2+
3+
BLANK = object()
4+
5+
6+
class HashTable:
7+
def __init__(self, capacity):
8+
self.values = capacity * [BLANK]
9+
10+
def __len__(self):
11+
return len(self.values)
12+
13+
def __delitem__(self, key):
14+
if key in self:
15+
self[key] = BLANK
16+
else:
17+
raise KeyError(key)
18+
19+
def __setitem__(self, key, value):
20+
self.values[self._index(key)] = value
21+
22+
def __getitem__(self, key):
23+
value = self.values[self._index(key)]
24+
if value is BLANK:
25+
raise KeyError(key)
26+
return value
27+
28+
def __contains__(self, key):
29+
try:
30+
self[key]
31+
except KeyError:
32+
return False
33+
else:
34+
return True
35+
36+
def get(self, key, default=None):
37+
try:
38+
return self[key]
39+
except KeyError:
40+
return default
41+
42+
def _index(self, key):
43+
return hash(key) % len(self)
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# test_hashtable.py
2+
3+
import pytest
4+
5+
from hashtable import HashTable, BLANK
6+
7+
8+
@pytest.fixture
9+
def hash_table():
10+
sample_data = HashTable(capacity=100)
11+
sample_data["hola"] = "hello"
12+
sample_data[98.6] = 37
13+
sample_data[False] = True
14+
return sample_data
15+
16+
17+
def test_should_create_hashtable():
18+
assert HashTable(capacity=100) is not None
19+
20+
21+
def test_should_report_capacity():
22+
assert len(HashTable(capacity=100)) == 100
23+
24+
25+
def test_should_create_empty_value_slots():
26+
assert HashTable(capacity=3).values == [BLANK, BLANK, BLANK]
27+
28+
29+
def test_should_insert_key_value_pairs():
30+
hash_table = HashTable(capacity=100)
31+
32+
hash_table["hola"] = "hello"
33+
hash_table[98.6] = 37
34+
hash_table[False] = True
35+
36+
assert "hello" in hash_table.values
37+
assert 37 in hash_table.values
38+
assert True in hash_table.values
39+
40+
assert len(hash_table) == 100
41+
42+
43+
def test_should_not_contain_none_value_when_created():
44+
assert None not in HashTable(capacity=100).values
45+
46+
47+
def test_should_insert_none_value():
48+
hash_table = HashTable(capacity=100)
49+
hash_table["key"] = None
50+
assert None in hash_table.values
51+
52+
53+
def test_should_find_value_by_key(hash_table):
54+
assert hash_table["hola"] == "hello"
55+
assert hash_table[98.6] == 37
56+
assert hash_table[False] is True
57+
58+
59+
def test_should_raise_error_on_missing_key():
60+
hash_table = HashTable(capacity=100)
61+
with pytest.raises(KeyError) as exception_info:
62+
hash_table["missing_key"]
63+
assert exception_info.value.args[0] == "missing_key"
64+
65+
66+
def test_should_find_key(hash_table):
67+
assert "hola" in hash_table
68+
69+
70+
def test_should_not_find_key(hash_table):
71+
assert "missing_key" not in hash_table
72+
73+
74+
def test_should_get_value(hash_table):
75+
assert hash_table.get("hola") == "hello"
76+
77+
78+
def test_should_get_none_when_missing_key(hash_table):
79+
assert hash_table.get("missing_key") is None
80+
81+
82+
def test_should_get_default_value_when_missing_key(hash_table):
83+
assert hash_table.get("missing_key", "default") == "default"
84+
85+
86+
def test_should_get_value_with_default(hash_table):
87+
assert hash_table.get("hola", "default") == "hello"
88+
89+
90+
def test_should_delete_key_value_pair(hash_table):
91+
assert "hola" in hash_table
92+
assert "hello" in hash_table.values
93+
assert len(hash_table) == 100
94+
95+
del hash_table["hola"]
96+
97+
assert "hola" not in hash_table
98+
assert "hello" not in hash_table.values
99+
assert len(hash_table) == 100
100+
101+
102+
def test_should_raise_key_error_when_deleting(hash_table):
103+
with pytest.raises(KeyError) as exception_info:
104+
del hash_table["missing_key"]
105+
assert exception_info.value.args[0] == "missing_key"

0 commit comments

Comments
 (0)