One way is to use the abstract base class collections.abc.MutableMapping
, this way, you only need to override the abstract methods and then you can be sure that access always goes through your logic -- you can do this with dict
too, but for example, overriding dict.__setitem__
will not affect dict.update
, dict.setdefault
etc... So you have to override those by hand too. Usually, it is easier to just use the abstract base class:
from collections.abc import MutableMapping
from enum import Enum
class Color(Enum):
RED = "RED"
GREEN = "GREEN"
BLUE = "BLUE"
class ColorDict(MutableMapping):
def __init__(self): # could handle more ways of initializing but for simplicity...
self._data = {}
def __getitem__(self, item):
return self._data[color]
def __setitem__(self, item, value):
color = self._handle_item(item)
self._data[color] = value
def __delitem__(self, item):
del self._data[color]
def __iter__(self):
return iter(self._data)
def __len__(self):
return len(self._data)
def _handle_item(self, item):
try:
color = Color(item)
except ValueError:
raise KeyError(item) from None
return color
Note, you can also add:
def __repr__(self):
return repr(self._data)
For easier debugging.
An example in the repl:
In [3]: d = ColorDict() # I want to implement a ColorDict class such that ...
...:
...: d[Color.RED] = 123
...: d["RED"] = 456 # I want this to override the previous value
...: d[Color.RED] # ==> 456
Out[3]: 456
In [4]: d["foo"] = 789 # I want this to produce an KeyError exception
...:
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-4-9cf80d6dd8b4> in <module>
----> 1 d["foo"] = 789 # I want this to produce an KeyError exception
<ipython-input-2-a0780e16594b> in __setitem__(self, item, value)
17
18 def __setitem__(self, item, value):
---> 19 color = self._handle_item(item)
20 self._data[color] = value
21
<ipython-input-2-a0780e16594b> in _handle_item(self, item)
34 color = Color(item)
35 except ValueError:
---> 36 raise KeyError(item) from None
37 return color
38 def __repr__(self): return repr(self._data)
KeyError: 'foo'
In [5]: d
Out[5]: {<Color.RED: 'RED'>: 456}
__setiitem__
,.update
, it may be easy enough. – Hittcollections.abc.MutableMapping
which would involve composition, but you would only have to implement a minimal amount of methods – HittKeyError
is a lookup error (something not found), but the assignment is not a lookup. I would consider theValueError
for a wrong key value, but to be honest, I'm not sure which one is more appropriate. – Halfwitted