# Special dictionary

I need a special dictionary that would do as follows:

• if key is set to more than once, the values are a list of all set values. Such a list is returned as value on get.

• if key is set only once, it is set to that value, and returned as value as well.

Sort of like MultiDict from Paste, but that does not seem to have the same interface as ordinary dict.

I wrote this:

class ModalDict(dict):

def __init__(self, *args, **kwargs):
class Speclist(list):
pass
self.speclist = Speclist
dict.__init__(self, *args, **kwargs)

def __getitem__(self, k):
v = dict.__getitem__(self, k)
if isinstance(v, self.speclist) and len(v) == 1:
return v[0]
return v

def __setitem__(self, k, v):
self.setdefault(k, self.speclist()).append(v)


Obviously I defined Speclist to differentiate it from regular list that someone may set as value.

Problems? Any better way to do this?

I think the second requirement (return the value for a key with only a single assignment as itself) is a bad requirement. It goes against Guido's guidance that passing different values (of the same type) to a function should avoid returning different types, and you can see how it breaks simple code like this, making it differ significantly from iterating d.items():

for k in d:       # assume d is an instance of your ModalDict
for v in d[k]:  # can't iterate simple values, or accidentally iterates list values
print(k, v)


Thus my recommendation would be to simply use d = collections.defaultdict(list) and always call d[k].append(value). If you need to prevent, say, accidentally replacing a list of values, you could make __setitem__ also do this or raise an exception. Since the implicit append seems like it would be surprising, I would rather raise an exception guiding towards the explicit append.

It can be solved in __setitem__, so the special __getitem__ will not affect the performance:

class ModalDict(dict):
def __setitem__(self, k, v):
if k in self:
if isinstance(self[k], Speclist):
v = self[k] + [v]
else:
v = Speclist([self[k], v])
dict.__setitem__(self,k,v)

class Speclist(list):
pass


If key is set only once, it is set to that value, and returned as value as well. So works like a normal dict:

>>> d = ModalDict()
>>> d['a'] = 1
>>> d['a']
1
>>> d # it prints the simple value too
{'a': 1}


If key is set to more than once, the values are a list of all set values:

>>> d['a'] = 2
>>> d['a']
[1, 2]
>>> d # it prints list too
{'a': [1, 2]}

>>> d['a'] = 3
>>> d['a']
[1, 2, 3]
>>> d
{'a': [1, 2, 3]}


Regular list is set as the value:

>>> d2 = ModalDict()
>>> d2['b'] = [1]
>>> d2['b']
[1]
>>> d2
{'b': [1]}


More values:

>>> d2['b'] = 2
>>> d2['b']
[[1], 2]
>>> d2
{'b': [[1], 2]}

>>> d2['b'] =  [3]
>>> d2['b']
[[1], 2, [3]]
>>> d2
{'b': [[1], 2, [3]]}