Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Why `__rsub__` subtract in reverse?

I have custom class that inherites from list. CustomList instances support subtraction between themselves and with regular lists. The corresponding elements of the CustomLists/lists are subtracted. If one of the CustomLists/lists is shorter, the missing elements are considered as zeros.

from typing import Iterable, List


class CustomList(list):
    def __init__(self, *args: Iterable[int | float]):
        if args == ():
            super().__init__(*args)
        elif all(isinstance(a, (int, float)) for a in args[0]):
            super().__init__(*args)
        else:
            raise TypeError("CustomList instance can contain int and float datatypes only")

    def __sub__(self, other: List[int | float] | "CustomList") -> "CustomList":
        if isinstance(other, (self.__class__, list)):
            result = CustomList()
            for i in range(max(len(self), len(other))):
                if i < len(self) and i < len(other):
                    result.append(self[i] - other[i])
                elif i < len(self):
                    result.append(self[i])
                else:
                    result.append(other[i])
            return result

    def __rsub__(self, other):
        return self.__sub__(other)

__sub__ works as expected, but when first operand is list (and __rsub__ is called) – subsctraction works in reverse. Instead of operand1 - operand2 it does operand2 - operand1.
For example:

[1, 2, 3] - CustomList([2, 4, 6]) sholud be CustomList([-1, -2, -3]) but I got CustomList([1, 2, 3]) instead.
Why does this happen?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

__rsub__ is called when you do list - CustomList, let’s say L - myList. when you define __rsub__ the way you did, you are returning the result of myList - L. But subtraction is not commutative (a - b != b - a).

My recommended solution is to overload the negation operator (__neg__) to define what - myList is. and then modify __rsub__ to return - self.__sub__(other).

Here is the full solution:

from typing import Iterable, List


class CustomList(list):
    def __init__(self, *args: Iterable[int | float]):
        if args == ():
            super().__init__(*args)
        elif all(isinstance(a, (int, float)) for a in args[0]):
            super().__init__(*args)
        else:
            raise TypeError("CustomList instance can contain int and float datatypes only")

    def __sub__(self, other: List[int | float] | "CustomList") -> "CustomList":
        if isinstance(other, (self.__class__, list)):
            result = CustomList()
            for i in range(max(len(self), len(other))):
                if i < len(self) and i < len(other):
                    result.append(self[i] - other[i])
                elif i < len(self):
                    result.append(self[i])
                else:
                    result.append(other[i])
            return result

    def __neg__(self):
        result = CustomList()
        for val in self:
            result.append(-val)
        return result

    def __rsub__(self, other):
        return - self.__sub__(other)
>>> [1, 2, 3] - CustomList([2, 4, 6])
[-1, -2, -3]
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading