0

I'm defining a Sqrt class to help with some complications in my code, since it's going to be used a lot. The class has a value val, which is the actual value of the square root being computed.

Code:

# Square root class (helps w accuracy and readability)
class Sqrt(object):

    def __init__(self, x: float) -> None:
        self.sqr_val = x
        self.val = math.sqrt(x)
    
    def __str__(self) -> str:
        return f"sqrt({self.sqr_val})"
    
    def __pow__(self, n: float) -> float:
        return self.sqr_val ** n / 2

The issue I'm facing is the need to define every dunder method ("magic method") to handle arithmetic operations like +, -, *, /, etc.:


class Sqrt(object):

    def __init__(self, x: float) -> None:
        self.sqr_val = x
        self.val = math.sqrt(x)
    
    def __str__(self) -> str:
        return f"sqrt({self.sqr_val})"
    
    def __pow__(self, n: float) -> float:
        return self.sqr_val ** n / 2
    
    def __add__(self, x):
        return self.val + x
    
    def __radd__(self, x):
        return self.val + x
    
    def __sub__(self, x):
        return self.val - x
    
    def __rsub__(self, x):
        return x - self.val
    
    def __mul__(self, x):
        return self.val * x
    
    def __rmul__(self, x):
        return self.val * x
    
    def __truediv__(self, x):
        return self.val / x

What I'd like is to be able to define these dunders implicitly, telling Python to use the normal int/float dunders for my class, but using self.val instead of self.

I have tried inheriting from int and using super() but that doesn't decrease repetition whatsoever. Is there a way to do this (maybe some fancy descriptor)? Or do I write every applicable dunder out manually?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Codeman
  • 477
  • 3
  • 13
  • (Without knowing the specifics of your use-case) I'm thinking what you're really looking for is [TypeAlias](https://docs.python.org/3/library/typing.html#typing.TypeAlias) or [NewType](https://docs.python.org/3/library/typing.html#newtype) and not a `Sqrt` class? Example: https://justincaustin.com/blog/python-typing-newtype/ – Kache May 31 '23 at 07:52

1 Answers1

2

The easiest would be to use a class that already implements them the way you want and subclass it:

class Sqrt(float):
    def __new__(cls, sqr):
        instance = super().__new__(cls, sqr ** .5)
        instance.sqr_val = sqr
        # instance.val = sqr ** .5  # if you you want to keep that attr
        return instance

    def __str__(self) -> str:
        return f"sqrt({self.sqr_val})"


>>> x = Sqrt(9)
>>> x
3.0
>>> x.sqr_val
9
>>> x + 5
8.0
>>> x / 2
1.5

The use of __new__ instead of (or alongside) __init__ is enforced by float being immutable, so you have to hook into the actual object creation. This is discussed here.

user2390182
  • 72,016
  • 6
  • 67
  • 89