I've created a subclass of Python's builtin dictionary dict. The subclass allows dictionaries to have multiple keys with the same value. Hence the name homogeneous. Behind the scenes, I'm using a normally dictionary. If a key has more than one value, then it's values are implemented with a list. Otherwise, the key, value pair is normal.Here are some example usages:>>> d = HomogeneousDict([('a', 1), ('b', 2), ('c', 3), ('b', 4)])>>> dHomogeneousDict({'c': 3, 'b': 2, 'b': 4, 'a': 1})>>> d['a']1>>> d['b'][2, 4]>>> d['a'] = 5>>> dHomogeneousDict({'c': 3, 'b': 2, 'b': 4, 'a': 1, 'a': 5})>>> d['a'][1, 5]>>> len(d)5>>> d['subdict'] = HomogeneousDict([('d', 6), ('e', 7)])>>> dHomogeneousDict({'c': 3, 'b': 2, 'b': 4, 'a': 1, 'a': 5, 'subdict': HomogeneousDict({'e': 7, 'd': 6})})>>> d['subdict'] = 8>>> dHomogeneousDict({'c': 3, 'b': 2, 'b': 4, 'a': 1, 'a': 5, 'subdict': HomogeneousDict({'e': 7, 'd': 6}), 'subdict': 8})>>> d['subdict'][HomogeneousDict({'e': 7, 'd': 6}), 8]>>> del d['subdict']>>> dHomogeneousDict({'c': 3, 'b': 2, 'b': 4, 'a': 1, 'a': 5})>>> d.pop('b', 1) # Pop the second value of the 'b' key4>>> dHomogeneousDict({'c': 3, 'b': 2, 'a': 1, 'a': 5})>>> for k, v in d.items():    print('{} => {}'.format(k, v))c => 3b => 2a => 1a => 5>>>I haven't given much thought if there is a practically usage for this type of dictionary. I really made this as more of a hobby project, rather than out of necessity. Here's the source code. I documented the important parts of the code, so their shouldn't be much to explain or figure out. It's actually only about 120 LOCs:class HomogeneousDict(dict):        About    -----    A dictionary that allows multiple keys to have    like values.    The dictionary supports all of the same methods of a normally    dictionary. The chief difference between a normally dictonary and    a HomogeneousDict, is when you get items.    If a key has be given multiple values, a list of all    of the values are returned for that key. Otherwise, the    key's value is returned normally as is.    Examaples    ---------    >>> d = HomogeneousDict([('a', 1), ('b', 2), ('c', 3), ('b', 4)])    >>> d    HomogeneousDict({'c': 3, 'b': 2, 'b': 4, 'a': 1})    >>> d['a']    1    >>> d['b']    [2, 4]    >>> d['a'] = 5    >>> d    HomogeneousDict({'c': 3, 'b': 2, 'b': 4, 'a': 1, 'a': 5})    >>> d['a']    [1, 5]    >>> len(d)    5    >>> d['subdict'] = HomogeneousDict([('d', 6), ('e', 7)])    >>> d    HomogeneousDict({'c': 3, 'b': 2, 'b': 4, 'a': 1, 'a': 5, 'subdict': HomogeneousDict({'e': 7, 'd': 6})})    >>> d['subdict'] = 8    >>> d    HomogeneousDict({'c': 3, 'b': 2, 'b': 4, 'a': 1, 'a': 5, 'subdict': HomogeneousDict({'e': 7, 'd': 6}), 'subdict': 8})    >>> d['subdict']    [HomogeneousDict({'e': 7, 'd': 6}), 8]    >>> del d['subdict']    >>> d    HomogeneousDict({'c': 3, 'b': 2, 'b': 4, 'a': 1, 'a': 5})    >>> d.pop('b', 1) # Pop the second value of the 'b' key    4    >>> d    HomogeneousDict({'c': 3, 'b': 2, 'a': 1, 'a': 5})    >>> for k, v in d.items():            print('{} => {}'.format(k, v))    c => 3    b => 2    a => 1    a => 5    >>>        def __init__(self, values):                Parameters        ----------        values : list          List of tuples that represent key, value pair                for key, value in values:            self.__setitem__(key, value)    def __setitem__(self, key, value):        if key in self:            if isinstance(self[key], list):                self[key].append(value)            else:                super().__setitem__(key, [self.pop(key), value])        else:            super().__setitem__(key, value)    def __getitem__(self, key):        return super().__getitem__(key)    def __len__(self):        count = 0        for key, value in super().items():            if isinstance(value, list):                count += len(value)            else:                count += 1        return count    def __repr__(self):        key_value_reprs = []        for key, value in super().items():            if isinstance(value, list):                for element in value:                    repr_ = '{}: {}'.format(key.__repr__(), element.__repr__())                    key_value_reprs.append(repr_)            else:                repr_ = '{}: {}'.format(key.__repr__(), value.__repr__())                key_value_reprs.append(repr_)        return 'HomogeneousDict({%s})' % ', '.join(key_value_reprs)    def pop(self, key, index=0):        if isinstance(self[key], list):            value = self[key].pop(index)            if len(self[key]) == 1:                super().__setitem__(key, self[key].pop())            return value        else:            return super().pop(key)    def items(self):        for key, value in super().items():            if isinstance(value, list):                for element in value:                    yield (key, element)            else:                yield key, valueThe biggest question I have is: How well did I emulate a Python container type. Is there anything unintuitive or non-normally for a dictionary-like container type? Also, is there a better name for a dictionary that behaves like this?
HomogeneousDict - The Python dictionary that can have homogeneous keys
python;dictionary
__repr__ should:look like a valid Python expression that could be used to recreate an  object with the same value (given an appropriate environment).Yours cannot possibly do that, as creating the vanilla dictionary loses all but one of the values for each key. Also note you can write:repr_ = '{!r}: {!r}'.format(key, element)In terms of emulating container types, you can look at the ABCs  to see what needs to be implemented. I would probably have approached this with composition rather than inheritance, leveraging the ABCs for automatic implementation of some methods:class HomogeneousDict(MutableMapping):This helps avoid missing important methods; for example, you don't provide a matching implementation of .values. In terms of naming, this is generally called a MultiDict. Given the examples, I'd add the following to the end of the file:if __name__ == '__main__':    import doctest     doctest.testmod()This won't impact anything importing the class, but means that the behaviour is validated against your expectations if you run the file directly, using doctest. This may mean tweaking things slightly to avoid issues with order, etc.; for example:File so.py, line 20, in __main__.HomogeneousDictFailed example:    dExpected:    HomogeneousDict({'c': 3, 'b': 2, 'b': 4, 'a': 1})Got:    HomogeneousDict({'a': 1, 'c': 3, 'b': 2, 'b': 4})You will also need to update multi-line examples with a leading ellipsis:>>> for k, v in d.items():...     print('{} => {}'.format(k, v))...c => 3