How to Make a Python Miltidict Class

In my previous post, I showed how to make a Python class immutable. This time around I will show you my attempt at making a "Multidict" class that behaves like a dictionary but can hold multiple values for each key. I accomplish this behavior by first inheriting the built in dict object and then changing some key methods to store more than one value per key.


Inheriting dict

By inheriting dict, we get a lot of the behaviors that we want for our Multidict. The following methods are all dict methods that we need to override in order to stop old values from getting replaced with new ones and instead stores all values for each key in a list.

class Multidict(dict):


__setitem__ Methods

The __setitem__ method controls the key assignment behavior which we need to change to not override the previous value but instead add a new value to the list of values if the key already exists.

     def __setitem__(self, key, value):
        if key in self:
            self[key].append(value)
        else:

            dict.__setitem__(self, key, [value])

__init__ and update Method

Dictionaries have a method called update that allows you to pass in either an iterable object or key word arguments (kwargs) in order to add all of the pairs to the dictionary. This is the same behavior we want to use when we initialize our Multidict so I wrote the update method then call it in __init__ in order to not repeat myself. 

    def update(self, iterable=(), **kwargs):
        """add kwargs or tuple iterable"""
        if iterable:
            if type(iterable) == dict:
                iterable = iterable.items()
            for key,val in iterable:
                self[key] = val
        if kwargs:
            for key,val in kwargs.items():
                self[key] = val


    def __init__(self, iterable=(), **kwargs):
        self.update(iterable, **kwargs)

__delitem__ Method

My __delitem__ method prevents all values from being deleted when there are more than one value stored for the key. It instead deletes the first value while leaving the rest. If only one value is stored, it uses the old dict __delitem__ method.

    def __delitem__(self, key):
        if len(self[key]) > 1:

            del self[key][0]
        else:
            dict.__delitem__(self, key)


pop and popitem Methods

For pop and popitem we also want to just affect the first item if there are more than one value for a key.

    def pop(self, key):
        if len(self[key]) > 1:
            temp = self[key][0]
            del self[key]
            return temp

        return dict.pop(self, key)

    def popitem(self, key):
        if len(self[key]) > 1:
            temp = key, [self[key][0]]
            del self[key][0]
            return temp
        return dict.popitem(self, key)

values Method

In order for our values method to output a list of just our values, we need to flatten out all our lists inside our Multidict keys.

    def values(self):
        return [val for vals in dict.values(self) for val in vals]


Comments

Post a Comment

Popular Posts