Python Reflected Operators: __radd__ and __rsub__
Python reflected operators are called when the left operand can’t handle a binary operation. See how __radd__ and __rsub__ work, when to implement them, and how to avoid common mistakes.
Python reflected operators, also known as reversed operators, are dunder methods that Python invokes when the left operand of a binary operation does not support the operation but the right operand does. Understanding these methods is essential for building custom numeric types or classes that interact with built-in types in a natural way.
How Python Dispatches Binary Operators
When you write an expression like a + b, Python first attempts to call a.__add__(b). If that method returns NotImplemented, or if it does not exist, Python then tries the reflected operation by calling b.__radd__(a). (There is an important subclass exception, described below.) This two-step dispatch applies to binary arithmetic and bitwise operators such as +, -, *, /, //, %, **, <<, >>, &, |, and ^.
For example, the reflected counterpart of __add__ is __radd__, of __sub__ is __rsub__, and so on. For __radd__(self, other), other is the left operand from the original expression, while self is the right operand (the instance on which the method is defined).
When the Reflected Operator Is Called
In the common case, the reflected operator is called only when the left operand's corresponding method returns NotImplemented or is absent. This behavior allows your class to define how it interacts with types it does not control, such as built-in numbers or other library classes.
Consider a custom Vector class that supports addition with a scalar. If you write vector + 5, Python calls vector.__add__(5). If you write 5 + vector, Python first tries int.__add__(vector), which returns NotImplemented, and then calls vector.__radd__(5). Without __radd__, the expression 5 + vector would raise a TypeError.
Implementing a Reflected Operator: Minimal Example
Here is a minimal implementation of a Vector class that supports addition with both another vector and a scalar, including the reflected version:
class Vector: def __init__(self, x, y): self.x = x self.y = y def __add__(self, other): if isinstance(other, Vector): return Vector(self.x + other.x, self.y + other.y) if isinstance(other, (int, float)): return Vector(self.x + other, self.y + other) return NotImplemented def __radd__(self, other): return self.__add__(other) def __repr__(self): return f'Vector({self.x}, {self.y})'
Now both Vector(1, 2) + 3 and 3 + Vector(1, 2) work. The reflected method simply delegates to __add__ because addition is commutative. For non-commutative operators like subtraction, you must implement the reflected version separately to get the correct operand order.
Returning NotImplemented and Type Fallback
Returning NotImplemented from a dunder method signals that the current type cannot handle the operation with the given operand. In the usual case, returning it from __add__ makes Python try the reflected method on the other operand. If neither operand can handle the operation, Python raises a TypeError with a message like unsupported operand type(s) for +: 'Vector' and 'str'.
A common mistake is to return False or None from an operator method when the type is unsupported. This is incorrect because Python will treat those as valid results, leading to confusing behavior. Always return NotImplemented when you cannot handle the operation.
When implementing reflected operators, keep track of which operand is which. The first argument is the left operand from the original expression, and self is the right operand. For example, in __rsub__, if you simply call self.__sub__(other), you will get the wrong operand order. You need to compute other - self explicitly.
Common Mistakes and Edge Cases
One frequent pitfall is infinite recursion. If a reflected method is implemented by writing self + other instead of an explicit method call, Python may dispatch through the operator machinery again and call the reflected method a second time. Use explicit calls such as self.__add__(other) and include type checks in both methods.
Another edge case involves subclasses. If the right operand's type is a subclass of the left operand's type and the subclass overrides the reflected method, Python gives that reflected method priority, even before trying the left operand's normal method. This is known as the 'right operand wins' rule for subclasses. You need to account for it when designing class hierarchies.
Performance and Maintainability Considerations
Reflected operators can add a small overhead because the dispatch may need to try the normal method and then fall back. Keep the methods concise and avoid redundant type checks in hot paths. In most applications this overhead is minor; focus on it only if profiling shows it matters.
From a maintainability perspective, implement reflected operators only when you genuinely need to support operations where your object appears on the right side of the operator. For commutative operations, you can often delegate to the normal method, but for non-commutative ones, you must write the correct logic. Document the operand order clearly to avoid confusion for other developers.
Advanced Example: Mixed-Type Arithmetic
Consider a Temperature class that stores Celsius values and supports scalar addition and subtraction. You might want both Temperature(20) + 5 and 5 + Temperature(20) to work. Subtraction is less symmetric: Temperature(20) - 5 should produce a Temperature of 15 degrees, while 5 - Temperature(20) should compute 5 - 20 and produce Temperature(-15). Here is an implementation:
class Temperature: def __init__(self, celsius): self.celsius = celsius def __add__(self, other): if isinstance(other, (int, float)): return Temperature(self.celsius + other) return NotImplemented def __radd__(self, other): return self.__add__(other) def __sub__(self, other): if isinstance(other, (int, float)): return Temperature(self.celsius - other) return NotImplemented def __rsub__(self, other): if isinstance(other, (int, float)): return Temperature(other - self.celsius) return NotImplemented def __repr__(self): return f'{self.celsius}°C'
Now Temperature(20) - 5 yields 15°C, while 5 - Temperature(20) yields -15°C. The reflected method explicitly computes other - self.celsius to preserve the correct operand order.
Reflected operators are a subtle but powerful part of Python's data model. They allow your classes to participate in arithmetic expressions with built-in types and third-party objects, provided you handle type checks and NotImplemented correctly. By understanding the dispatch order and implementing these methods deliberately, you avoid common errors and create APIs that feel native to Python.