a+b
is equivalent to import operator; operator.add(a,b)
. It starts by calling a.__add__(b)
and then, if necessary, b.__radd__(a)
. But ifsubclass(type(b), type(a))
, then b.__radd__(a)
is called first.
Based on the docs on "special" methods:
Regarding __add__()
:
__add__()
is called to implement the binary arithmetic "+" operation. For instance, to evaluate the expression x + y, where x is an instance of a class that has an __add__()
method, x.__add__(y)
is called.
If one of those methods does not support the operation with the supplied arguments, it should return NotImplemented.
Regarding __radd__()
:
These functions are only called if the left operand does not support the corresponding operation and the operands are of different types. For instance, to evaluate the expression x + y, where y is an instance of a class that has an __radd__()
method, y.__radd__(x)
is called if x.__add__(y)
returns NotImplemented.
If the right operand’s type is a subclass of the left operand’s type and that subclass provides the reflected method for the operation, this method will be called before the left operand’s non-reflected method. This behavior allows subclasses to override their ancestors’ operations.
Explanation with the examples based on the behaviour:
Case 1:
>>> print 1+c
('C.__radd__', <__main__.C instance at 0x7ff5631397a0>, 1)
reversed result
These functions radd
are only called if the left operand does not support the corresponding operation and the operands are of different types. In this case, 1
does not support addition of the class hence, it falls back to the __radd__()
function of the C
class. In case __radd__
was not implement in C()
class, it would have fallen back to __add__()
Case2:
>>> 1 .__add__(c)
NotImplemented
>>> c .__add__(1)
('C.__add__', <__main__.C instance at 0x7ff563139830>, 1)
'result'
1 .__add__(c)
gives NotImplemented
as 1
is of int
type and add
of int
class do not supports add
with C class object. But c .__add(1)
run because C()
class supports that.
Case 3:
>>> int.__add__(1, c)
NotImplemented
>>> C.__add__(c, 1)
('C.__add__', <__main__.C instance at 0x7ff5610add40>, 1)
'result'
Similar to case 2
. But here, the call is made via class with first argument as object of that class. Behaviour would be same.
Case 4:
>>> int.__add__(c, 1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: descriptor '__add__' requires a 'int' object but received a 'instance'
>>> C.__add__(1, c)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method __add__() must be called with C instance as first argument (got int instance instead)
Vice-versa of case 3
. As is cleared from the stack-trace, __add__
expected the object of the calling class as the first argument, failing which resulted in exception.