Why adding a float to an integer using __add__ or __radd__ returns NotImplemented

429 Views Asked by At

I previously asked the same question of why 10 .__add__(5.5) and 10. __radd__ (5.5) return NotImplemented but the question was closed due to possible duplicates.

The answer of one of the "duplicates" state that:

a + b does not directly translate to a.__add__(b). It also tries b.__radd__(a) if a.__add__ doesn't exist or returns NotImplemented.

That answer suggests that either __add__ or __radd__ should work, but neither __add__ nor __radd__ really work as demonstrated in my code above.

The answer of the other "duplicate" state that:

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)

Again, neither __add__ nor __radd__ do the job. So, those answers are basically paraphrasing the Python docs on special methods which state that when a + operator occurs __add__ is called first and then __radd__ if __add__ was not successful. What happens when __radd__ is not successful either?

1

There are 1 best solutions below

4
MSeifert On

I think you didn't realize that the operators are swapped for the reverse operation. It's b.__radd__(a) - it calls __radd__ on the other operator!

So because 10 .__add__(5.5) returns NotImplemented it calls 5.5 .__radd__(10) (which returns 15.5) not 10. __radd__ (5.5). If the __radd__ on the swapped operators also returns NotImplemented the Python would raise an appropriate TypeError.

This snippet demonstrates how the methods are called for + (and what happens if both return NotImplemented):

class T:
    def __init__(self, value):
        self._value = value

    def __repr__(self):
        return f"{type(self).__name__}({self._value})"

    def __add__(self, other):
        print(f'called {self!r}.__add__({other!r})')
        return NotImplemented

    def __radd__(self, other):
        print(f'called {self!r}.__radd__({other!r})')
        return NotImplemented

class U(T):
    ...

>>> T(1) + U(2)
called T(1).__add__(U(2))
called U(2).__radd__(T(1))

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
---> 19 T(1) + U(2)
TypeError: unsupported operand type(s) for +: 'T' and 'U'

Because __radd__ is only called if the class of the operands differ I needed to create a second class in this example.

However you really shouldn't call special methods directly, if you need a function to perform such an operation you could use operator.add in your case.