9

I want to create a class that wraps an int and allows some things not normally allowed with int types. Here is my code:

class tInt(int): def __add__(self, other): if type(other) == str: return str(self) + str(other) elif type(other) == int: return int(self) + other elif type(other) == float: return float(self) + float(other) else: return self + other a = tInt(2) print (a + "5") print ("5" + a) 

The output was:

25 Traceback (most recent call last): File "C:\example.py", line 14, in <module> print ("5" + a) TypeError: Can't convert 'tInt' object to str implicitly 

So, the first print statement ran nicely, and gave what I expected, but the second one gave an error. I think this is because the first one is using tInt.__add__ because a appeared before + "5" and the second one is using str.__add__ because "5" appeared first. I know this but I don't really know how to either force a.__add__ to be used or allow the tInt class to be represented as a string/int/etc. when a normal type appears before it in an operation.

3
  • 4
    The else clause will cause infinite recursion. self + other will call self.__add__(other) again. Commented Apr 20, 2016 at 13:51
  • Damn, I didn't think of that. So I should sort of "sanitize" all my other ones by making sure self is a different type? Commented Apr 20, 2016 at 16:58
  • 2
    Use return NotImplemented instead. This will instruct Python to either try tInt.__radd__(other) if it is defined, or to try type(other).__add__(self) if not. Commented Apr 20, 2016 at 17:06

1 Answer 1

13

You need to implement an __radd__ method to handle the case when an instance of your class is on the right hand side of the addition.

The docs say:

These methods are called to implement the binary arithmetic operations (+, -, *, @, /, //, %, divmod(), pow(), **, <<, >>, &, ^, |) with reflected (swapped) operands. 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 __rsub__() method, y.__rsub__(x) is called if x.__sub__(y) returns NotImplemented.

Example:

class tInt(int): def __add__(self, other): if isinstance(other, str): return str(self) + str(other) elif isinstance(other, int): return int(self) + other elif isinstance(other, float): return float(self) + float(other) else: return NotImplemented def __radd__(self, other): return self.__add__(other) a = tInt(2) for x in ["5", 5, 5.0]: print(a + x) print(x + a) print() 
25 25 7 7 7.0 7.0 

As @chepner pointed out in the comments, returning NotImplemented for cases that your method doesn't handle will cause Python to try other ways of performing the operation, or raise a TypeError if there is no way to perform the requested operation.

In the above code, the implementation of __radd__ is trivial because integer addition is associative, that is

2 + 3 == 3 + 2 

Addition of other types may not be associative, in which case __radd__ will need to do more than just delegate to __add__:

'a' + 'b' != 'b' + 'a' [0, 1] + [2, 3] != [2, 3] + [0, 1] 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.