Let's for the sake of example assume I want to subclass dict
and have all keys capitalized:
class capdict(dict):
def __init__(self,*args,**kwds):
super().__init__(*args,**kwds)
mod = [(k.capitalize(),v) for k,v in super().items()]
super().clear()
super().update(mod)
def __getitem__(self,key):
return super().__getitem__(key.capitalize())
def __setitem__(self,key,value):
super().__setitem__(key.capitalize(),value)
def __delitem__(self,key):
super().__detitem__(key.capitalize())
This works to an extent,
>>> ex = capdict(map(reversed,enumerate("abc")))
>>> ex
{'A': 0, 'B': 1, 'C': 2}
>>> ex['a']
0
but, of course, only for methods I remembered to implement, for example
>>> 'a' in ex
False
is not the desired behavior.
Now, the lazy way of filling in all the methods that can be derived from the "core" ones
would be mixing in collections.abc.MutableMapping
. Only, it doesn't work here. I presume because the methods in question (__contains__
in the example) are already provided by dict
.
Is there a way of having my cake and eating it? Some magic to let MutableMapping
only see the methods I've overridden so that it reimplements the others based on those?
What you could do:
This likely won't work out well (i.e. not the cleanest design), but you could inherit from MutableMapping first and then from dict second.
Then MutableMapping would use whatever methods you've implemented (because they are the first in the lookup chain):
>>> class D(MutableMapping, dict):
def __getitem__(self, key):
print(f'Intercepted a lookup for {key!r}')
return dict.__getitem__(self, key)
>>> d = D(x=10, y=20)
>>> d.get('x', 0)
Intercepted a lookup for 'x'
10
>>> d.get('z', 0)
Intercepted a lookup for 'z'
0
Better way:
The cleanest approach (easy to understand and test) is to just inherit from MutableMapping and then implement the required methods using a regular dict as the base data store (with composition rather than inheritance):
>>> class CapitalizingDict(MutableMapping):
def __init__(self, *args, **kwds):
self.store = {}
self.update(*args, **kwds)
def __getitem__(self, key):
key = key.capitalize()
return self.store[key]
def __setitem__(self, key, value):
key = key.capitalize()
self.store[key] = value
def __delitem__(self, key):
del self.store[key]
def __len__(self):
return len(self.store)
def __iter__(self):
return iter(self.store)
def __repr__(self):
return repr(self.store)
>>> d = CapitalizingDict(x=10, y=20)
>>> d
{'X': 10, 'Y': 20}
>>> d['x']
10
>>> d.get('x', 0)
10
>>> d.get('z', 0)
0
>>> d['w'] = 30
>>> d['W']
30
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With