I'm trying to understand the exact mechanism behind updating a python dictionary using d[key] += diff. I have some helper classes to trace magic method invocations:
class sdict(dict):
def __setitem__(self, *args, **kargs):
print "sdict.__setitem__"
return super(sdict, self).__setitem__(*args, **kargs)
def __delitem__(self, *args, **kargs):
print "sdict.__delitem__"
return super(sdict, self).__delitem__(*args, **kargs)
def __getitem__(self, *args, **kargs):
print "sdict.__getitem__"
return super(sdict, self).__getitem__(*args, **kargs)
def __iadd__(self, *args, **kargs):
print "sdict.__iadd__"
return super(sdict, self).__iadd__(*args, **kargs)
def __add__(self, *args, **kargs):
print "sdict.__add__"
return super(sdict, self).__add__(*args, **kargs)
class mutable(object):
def __init__(self, val=0):
self.value = val
def __iadd__(self, val):
print "mutable.__iadd__"
self.value = self.value + val
return self
def __add__(self, val):
print "mutable.__add__"
return mutable(self.value + val)
With these tools, let's go diving:
>>> d = sdict()
>>> d["a"] = 0
sdict.__setitem__
>>> d["a"] += 1
sdict.__getitem__
sdict.__setitem__
>>> d["a"]
sdict.__getitem__
1
We don't see any __iadd__ operation invoked here, which makes sense because the left-hand side expression d["a"] returns an integer that does not implement the __iadd__ method. We do see python magically converting the += operator into __getitem__ and __setitem__ calls.
Continuing:
>>> d["m"] = mutable()
sdict.__setitem__
>>> d["m"] += 1
sdict.__getitem__
mutable.__iadd__
sdict.__setitem__
>>> d["m"]
sdict.__getitem__
<__main__.mutable object at 0x106c4b710>
Here the += operator successfully invokes an __iadd__ method. It looks like the += operator is actually being used twice:
- Once for the magic translation to
__getitem__and__setitem__calls - A second time for the
__iadd__call.
Where I need help is the following:
- What is the exact, technical mechanism for translating the
+=operator into__getitem__and__setitem__calls? - In the second example, why is the
+=operator used twice? Doesn't python translate the statement tod["m"] = d["m"] + 1(In which case wouldn't we see__add__be invoked instead of__iadd__?)