Python OrderedDict __setitem__ overloading
<p>I'm building a class that inherits OrderedDict, in which every key returns a list. I'm looking to overload <strong>setitem</strong> such that if the key doesn't exist, new assignments immediately put the value into a list, otherwise the new value is appended to the list. The following seems to be working:</p>

<pre><code>from collections import OrderedDict

class ListDict(OrderedDict):
    def __init__(self):
        super(ListDict, self).__init__()

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

thedict = ListDict()

thedict['a'] = 'first item'
thedict['b'] = 'another first item'
thedict['a'] = 'a second item?'

print thedict
</code></pre>

<p>which prints:</p>

<pre><code>$ python inheritex.py
ListDict([('a', ['first item', 'a second item?']), ('b', ['another first item'])])
</code></pre>

<p>Instead of appending with an assignment operator '=', I would rather have new items be appended with '+=', or even something like:</p>

<pre><code>ListDict['key'] = ListDict['key'] + 'value'
</code></pre>

<p>How would one go about overloading this? Besides being possible to monkey patch an add function, is it even a Pythonic/readable means to alter class behavior, or is this acceptable because the inherited function (OrderedDict) is untouched?</p>

<p>You can already use <code>+=</code> on <em>existing</em> keys:</p>

<pre><code>&gt;&gt;&gt; from collections import OrderedDict
&gt;&gt;&gt; thedict = OrderedDict()
&gt;&gt;&gt; thedict['a'] = []  # set initial list
&gt;&gt;&gt; thedict['a'] += ['foo']
&gt;&gt;&gt; thedict['a'] += ['bar']
&gt;&gt;&gt; thedict
OrderedDict([('a', ['foo', 'bar'])])
</code></pre>

<p>Note that <code>+=</code> on a list is basically the same thing as <code>list.extend()</code>, so you need to append lists.</p>

<p>If you want this to work for keys that don't exist yet, implement a <a href="https://docs.python.org/3/reference/datamodel.html#object.__missing__" rel="nofollow"><code>__missing__</code> method</a>, rather than <code>__setitem__</code>:</p>

<pre><code>class ListDict(OrderedDict):
    def __missing__(self, key):
        self[key] = []
        return self[key]
</code></pre>

<p>When a key is missing during <code>dict[key]</code> lookup, <code>__missing__</code> is called and it's return value is returned instead of raising a <code>KeyError</code>.</p>

<p>Now both <code>+</code> and <code>+=</code> work on missing keys too:</p>

<pre><code>&gt;&gt;&gt; thedict = ListDict()
&gt;&gt;&gt; thedict['a'] += ['foo', 'bar']
&gt;&gt;&gt; thedict['b'] = thedict['b'] + ['spam', 'ham']
&gt;&gt;&gt; thedict
ListDict([('a', ['foo', 'bar']), ('b', ['spam', 'ham'])])
</code></pre>

<p>If you concatenation must work without adding lists, you could produce a custom list subclass too:</p>

<pre><code>class ConcatList(list):
    def __add__(self, value):
        return type(self)(super(ContactList, self).__add__([value]))
    def __iadd__(self, value):
        self.append(value)
        return self
</code></pre>

<p>then use that type in <code>__missing__</code> (and convert any new lists set directly in <code>__setitem__</code>):</p>

<pre><code>class ListDict(OrderedDict):
    def __missing__(self, key):
        self[key] = ConcatList()
        return self[key]
    def __setitem__(self, key, value):
        if not isinstance(key, ConcatList):
           value = ConcatList([value])
        super(ListDict, self).__setitem__(key, value)
</code></pre>

<p>after which you can forgo brackets:</p>

<pre><code>&gt;&gt;&gt; thedict = ListDict()
&gt;&gt;&gt; thedict['a'] += 'foo'
&gt;&gt;&gt; thedict['b'] = thedict['b'] + 'bar'
&gt;&gt;&gt; thedict
ListDict([('a', ['foo']), ('b', ['bar'])])
</code></pre>

python dictionary overloading ordereddictionary