Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

__add__ returns instance of superclass not subclass

When I subclass some class, say int, and customise it's __add__ method and call super().__add__(other) it returns an instance of int, not my subclass. I could fix this by adding type(self) before every super() call in every method that returns an int, but that seems excessive. There must be a better way to do this. The same thing happens with floats and fractions.Fractions.

class A(int):
    def __add__(self, other):
        return super().__add__(other)

x = A()
print(type(x + 1))

Output:
<class 'int'>

Expected Output:
<class '__main__.A'>

like image 640
RedKnite Avatar asked Dec 08 '25 17:12

RedKnite


2 Answers

One approach would be to create a decorator that could wrap the desired math operations with the cast:

def wrap_math(c):
    def wrapped(orig):
        return lambda s, o: c(orig(s,o))

    maths = ["__add__", "__sub__"]
    for op in maths:
        func = wrapped(getattr(c, op))
        setattr(c, op, func)

return c

@wrap_math
class Special(int)
    pass

 x = Special(10)
 type(x + 10)

Complete the list of functions you want to wrap and you should be good to go.

like image 194
mageos Avatar answered Dec 11 '25 05:12

mageos


This can be done using a descriptor. The following class uses special methods that have a special effect when that class is instantiated inside class body.

class SuperCaller:

    def __set_name__(self, owner, name):
        """Called when the class is defined. owner is the class that's being
        defined. name is the name of the method that's being defined.
        """
        method = getattr(super(owner, owner), name)
        def call(self, other):
            # Note that this self shadows the __set_name__ self. They are two
            # different things.
            return type(self)(method(self, other))
        self._call = call

    def __get__(self, instance, owner):
        """instance is an instance of owner."""
        return lambda other: self._call(instance, other)


class A(int):
    __add__ = SuperCaller()


x = A()
print(type(x + 1))

Output: <class '__main__.A'>

like image 32
luther Avatar answered Dec 11 '25 05:12

luther



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!