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__
?)
sdict.__iadd__
is never being invoked because you don't have any code that performs+=
on ansdict
object. You're only performing+=
on elements in thesdict
-- in the first case an integer and in the second case amutable
. But you already demonstrated that you understand this, so I'm not sure what the question is. – Leonardoleoncavallo