How to implement the __missing__ method in Python




Suppose that you want to implement a dictionary for your application that is capable of mapping string keys as well as integer keys. Or perhaps you want to handle mappings of missing keys in a way that won't disrupt the program execution. Well, Python's got you covered.

The __missing__ method is provided as a way to handle key misrepresentation or to provide that extra flexibility you need when hashing dictionaries. Consider a case where you'd like to convert your mappings to type str before looking them up.


>>> dic = myDict([('10','ten'), ('4', 'four')])
>>> dic[10]
'ten'
>>> dic['10']
'ten'



>>> dic.get('4')

'four'

>>> dic.get(4)

'four'

This __missing__ method is an abstract method of the dict class. If you don't know what an abstract method/function is, it's simply one that is defined within a class for the purpose of it to be overwritten. What's the reason of defining this particular abstract method if it's only going to be overwritten? Good question. If you provide your own definition for the __missing__ method, the standard dict.__getitem__ will call it whenever a key is not found, instead of raising a KeyError

Simply put, this method is provided for you to control indexing and/or slicing of the dictionary. 


class myDict(dict):

    def __missing__(self,key):
        if isinstance(key, str):
            raise KeyError


        return self[str(key)]

First, notice that myDict inherits from dict. The second line is provided because if the user has indexed a string value that is missing. Going on, if the indexing is not a str, then i can assume (in this particular case only) that the indexing provided is of integer type. So the code builds a str from key and looks it up.

If you feel unsure as to why the if isinstance(key, str)line is included, try to imagine the different values that a user may try and use when indexing. Don't forget that even if the indexing is of type string, it may not also exist. 



In [0]: d = myDict([('0', 'zero'), ('1', 'one')])

In [1]: d['0']
Out[1]: 'zero'

In [2]: d[1]
Out[2]: 'one'

In [3]: d['2']

... Traceback (most recent call last) KeyError   

Defining the __missing__ method alone is not enough to maintain consistent behavior as the one above. Because we've inherited the dict class, any indexing calls the __contains__ method of the dict class. However, the method inherited from dict does not fall back to invoking the __missing__ method.


def __contains__(self, key):


        return key in self.keys() or str(key) in self.keys()

The check in the contains method is necessary because our class does not assert that all keys used to be string typed.

A workaround to avoid such inconsistency is inherit UserDict instead of dict.

UserDict 

If we choose to use UserDict, we'll actually end up with a shorter and easier implementation. UserDict automatically stores all of it's keys as string type, even if the user inputs them differently. Therefore, our __contains__ method can be implemented this way 



def __contains__(self, key):


        return str(key) in self.data()

The variable self.data is a dict instance within UserDict. You don't have to understand its full usage or why it occurs exactly. Just know that it holds all the current items of the dictionary, and it isolates us from any accidental recursion when overwriting methods like __setitem__, and makes __contains__ operation much simpler. The final implementation would look something like this ...


class myDict(UserDict):

    def __missing__(self,key):
        if isinstance(key, str):
            raise KeyError
        return self[str(key)]

    def __setitem__(self, key, item):
        return str(key) in self.data

    def __contains__(self, key):


        return key in self.keys() or str(key) in self.keys()