Search code examples
pythondictionarypython-2.xdictionary-missing

Python 2 __missing__ method


I wrote a very simple program to subclass a dictionary. I wanted to try the __missing__ method in python. After some research i found out that in Python 2 it's available in defaultdict. ( In python 3 we use collections.UserDict though..) The __getitem__ is the on responsible for calling the __missing__ method if the key isn't found.

When i implement __getitem__ in the following program i get a key error, but when i implement without it, i get the desired value.

import collections
class DictSubclass(collections.defaultdict):

    def __init__(self,dic):
        if dic is None:
            self.data = None
        else:
            self.data = dic

    def __setitem__(self,key,value):
        self.data[key] = value

    ########################
    def __getitem__(self,key):
        return self.data[key]
    ########################

    def __missing__(self,key):
        self.data[key] = None

dic = {'a':4,'b':10}
d1 = DictSubclass(dic)
d2 = DictSubclass(None)    
print  d1[2]

I thought i needed to implement __getitem__ since it's responsible for calling __missing__. I understand that the class definition of defaultdict has a __getitem__ method. But even so, say i wanted to write my own __getitem__, how would i do it?


Solution

  • The dict type will always try to call __missing__. All that defaultdict does is provide an implementation; if you are providing your own __missing__ method you don't have to subclass defaultdict at all.

    See the dict documentation:

    d[key]
    Return the item of d with key key. Raises a KeyError if key is not in the map.

    If a subclass of dict defines a method __missing__() and key is not present, the d[key] operation calls that method with the key key as argument. The d[key] operation then returns or raises whatever is returned or raised by the __missing__(key) call. No other operations or methods invoke __missing__().

    However, you need to leave the default __getitem__ method in place, or at least call it. If you override dict.__getitem__ with your own version and not call the base implementation, __missing__ is never called.

    You could call __missing__ from your own implementation:

    def __getitem__(self, key):
        if key not in self.data:
            return self.__missing__(key)
        return self.data[key]
    

    or you could call the original implementation:

    def __getitem__(self, key):
        if key not in self.data:
            return super(DictSubclass , self).__getitem__(key)
        return self.data[key]
    

    In Python 2, you can just subclass UserDict.UserDict:

    from UserDict import UserDict
    
    class DictSubclass(UserDict):
        def __missing__(self, key):
            self.data[key] = None