"magic" method
If you're learning the data structures in Python, I would read about the python data model and Collections abstract base classes to see what magic methods you can/should implement. A Hashmap is a mapping, so it should implement __getitem__
, __iter__
, __len__
, __contains__
, keys
, items
, values
, get
, __eq__
, and __ne__
return vs exception
In case your mapping does not find a key, it returns "This key cannot be found."
. This means users of your code should check against this sentinel value when retrieving something. What if that is the value they want to store in this mapping? The correct way to handle a missing key, is to raise an Exception. More specifically a KeyError
range
for i in range(0,1)
is equivalent to range(1)
, so it only yields 0, which means that in this list comprehension it does nothing.
[[] for j in range(self.size)]
Would hav achieved the same. [[]] * self.size
would not have worked, since the reference to the same inner list would have been copied.
variable names
size
is not the size of the mapping, but the size of the hash table, so this name might be confusing. Moreover, this should not be a public variable of the, so _hashtable_size
would be more appropriate.
According to PEP-8, variable names should be snake_case
, so mainList
would be _main_list
get
For a dict and a mapping, get
has another argument default
, which gets returned if the key is missing
iteration
In python, it is seldomly necessary to iterate over the index , like you do in for i in range(0,len(self.mainList[index])):
for dict_key, value in self.mainList[index]:
if dict_key == key:
return value
achieves the same, but is a lot more clearer and concise
docstrings
Python has a convention on how to document a method. It's called docstrings. Instead of a #
, you use a """
def put(self, key, value):
"""To add value to the list."""
class Hashtable:
def __init__(self, size):
"""explanation what this class does, and what the argument means"""
self._hastable_size = size
self._main_list = [[] for j in range(size)]
def _index(self, key):
# To get the index number.
return sum(ord(i) for i in key) % self._hastable_size
def __contains__(self, key):
index = self._index(key)
return any(dict_key == key for dict_key in self._main_list[index])
def put(self, key, value):
"""To add value to the list."""
if key in self: # calls self.__contains__(key)
raise ValueError(f"<{key}> already present")
# you can also choose to overwrite the already present value
index = self._index(key)
self._main_list[index].append((key, value))
__setitem__ = put
def __getitem__(self, key):
if key not in self:
raise KeyError(f"<{key}> not present")
index = self._index
for dict_key, value in self._main_list[index]:
if dict_key == key:
return value
def get(self, key, default=None):
# To get value from the list
try:
return self[key]
except KeyError:
return default
def __len__(self):
return sum(len(sublist) for sublist in self._main_list)
...