I'm teaching myself Python when I run into the __rsub__
method. While I can find explanation on the method in the official documentation:
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 expressionx - y
, wherey
is an instance of a class that has an__rsub__()
method,y.__rsub__(x)
is called ifx.__sub__(y)
returnsNotImplemented
.
I can't picture why the method is necessary and how exactly it is used in reality.
Would you kindly give me a typical environment where the method is useful?
Basic example. You write your own int
-like class:
class FooInt:
... other stuff elided ...
def __sub__(self, other):
if isinstance(other, FooInt):
return self.__class__(self.intvalue - other.intvalue)
elif isinstance(other, int):
return self.__class__(self.intvalue - other)
else:
return NotImplemented
Now you have code that does:
FooInt(123) - 456
This works fine; Python sees FooInt
on the left side, sees it has __sub__
, and calls FooInt.__sub__(FooInt(123), 456)
which returns without error, and we're good.
Next we see:
123 - FooInt(456)
Python tries calling int.__sub__(123, FooInt(456))
, but int
has no idea how to handle a FooInt
, and returns NotImplemented
; it has no idea that intvalue
has a value it can use for this purpose. At this point, Python can't just call FooInt.__sub__(FooInt(456), 123)
because it can't assume subtraction is commutative (and in fact, as in most numerical systems, subtraction is not commutative in this case, you can't just swap the right and left sides of the operator and get the correct result). This is why __rsub__
exists; it lets you check the other class for a means of handling the operation while telling it two things:
The second point is also very important. When implementing __xxx__
(the left hand operator) you want to be very conservative on the types you recognize. If you don't know for a fact you're working with a known concrete type with a known correct handler, you shouldn't try to handle unknown types; the other type might know how to do the operation correctly, so you return NotImplemented
and let the other side handle it. When you're implementing __rxxx__
, you're the last chance; the other guy didn't know what to do, so you should be liberal in what you accept and do your best if you have any means of handling it. You can see this in action in the Python docs on Implementing the arithmetic operations; the __xxx__
operations check for concrete types (for Fraction
, it checks for Fraction
, int
, float
, and complex
), while the __rxxx__
operators check for general interfaces (numbers.Integral
, numbers.Rational
, numbers.Real
, etc.).
I've glossed over one point that is relatively important: If the class on the right side is a subclass of the class on the left, it has its reflected __rxxx__
method called first; the assumption is that the subclass will have more information to correctly determine what to do, so it's given the first stab at performing the operation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With