6

Would be very easy to solve had python been a static programming language that supported overloading. I am making a class called Complex which is a representation of complex numbers (I know python has its own, but i want to make one myself), where a is the real number and b is the imaginary (Complex(a, b)). It should support adding Complex instances together (Complex(2, 4) + Complex(4, 5) = Complex(6, 9)), as well as adding an integer (Complex(2, 3) + 4 = Complex(6, 3)). However, due to the nature of python...

__add__(self, other):

...I have to choose which the class will support, because it won't recognize types at compile-time, as well as not supporting overloading of functions. What is the best solution? Do I have to write an if statement in relation to the datatype of the other parameter?

DjaouadNM
  • 22,013
  • 4
  • 33
  • 55
Snusifer
  • 485
  • 3
  • 17
  • If you want to support adding objects of different types, the nicest option in my opinion is to use [single dispatcher](https://docs.python.org/3/library/functools.html#functools.singledispatch), although it might be a bit of an overkill here on second thought. Try it out and see what do you prefer :) – Kacperito Sep 06 '19 at 23:40

5 Answers5

2

What you could do is check for the thing being of instance Complex, and if not, turn it into one, like so:

def __add__(self, other):
    if isinstance(other, Complex):
        # do addition
    else:
        return self + Complex(other, 0)

That of course does not eliminate type checking, but it reuses whatever you are doing in __init__ (which is probably checking if input is int or float).

If at the moment you do not do type checking in init, it is probably a good idea, and this looks reasonable, excepting built-in complex type.

Andrei
  • 55,890
  • 9
  • 87
  • 108
1

Mixed-types operations

The numbers module in Python can be used to implement you own number classes. Among other things it allows to correctly implement mixed-types operations using __add__ and __radd__

Example

import numbers

class Complex:
    def __add__(self, other):
        if isinstance(self, Complex):
            ...
        elif isinstance(other, numbers.Real):
            ...
        else:
            raise TypeError

    def __radd__(self, other):
        return self + other

Implemention new number types

If you want to implement a number class which works along with Python built-in number types, you can implement your own Complex class by subclassing the abstract base class numbers.Complex.

This abstract base class will enforce the implementation of the needed methods __abs__, __add__, __complex__, __eq__, __mul__, __neg__, __pos__, __pow__, __radd__, __rmul__, __rpow__, __rtruediv__, __truediv__, conjugate, imag and real.

Olivier Melançon
  • 21,584
  • 4
  • 41
  • 73
1

Use isinstance to check if it's the same Type if not assum it's any type of number:

  def __add__(self, other):
       # it's the same class
       if isinstance(other, Complex):
            # and you should return the same class 
            # if anyone extend your class SomeClass(Complex): you should return SomeClass not Complex Object
            return self.__class__(self.a + other.a, self.b + other.b)

       # assuming is any type of number
       try:
         return self.__class__(int(self.a + other), self.b)
       except TypeError:
         # change the error message
         raise TypeError("unsupported operand type(s) for +: '%s' and '%s'" % (self.__class__, other.__class__)
Charif DZ
  • 14,415
  • 3
  • 21
  • 40
  • 1
    That's `__iadd__`, the in-place add. Depending on whether Complex numbers are supposed to be modifiable in place, or not, you may or may not want that one. – torek Sep 06 '19 at 23:22
  • Yes I forget that __add__ should return an object of the same Class ^^ – Charif DZ Sep 06 '19 at 23:23
  • when returning make sure to return an object of the same class not Complex class – Charif DZ Sep 06 '19 at 23:27
  • If you want Complex to be sub-class-able without forcing the subclass to provide its own functions, yes, use `self.__class__` to construct the return value. – torek Sep 06 '19 at 23:31
  • It's always recommended To use this technique it's safer. – Charif DZ Sep 06 '19 at 23:33
1

There is not necessarily a best solution. In this particular case, though:

def __add__(self, other):
    c = make_complex(other)
    return Complex(self.real + c.real, self.imag + real.imag)

is probably the way to go (though I'm making lots of assumptions about your Complex class here). If other is already Complex, the make_complex function returns it. If not, it tries its best to convert (e.g., to turn a real-only into a complex pair by constructing a complex with a zero imaginary part). If that fails, it raises some suitable exception.

This make_complex is also suitable in the constructor for Complex, so that you can replace parts of:

e = Complex(1.718, 0) # e (well, not very exactly)
i = Complex(0, 1) # sqrt(-1)
pi = Complex(3.14, 0) # pi
# you know what to do next

with:

e = Complex(1.718)
pi = make_complex(3.14)

for instance. (You can just use the Complex constructor to do all the work, using isinstance() to check the argument types as appropriate.)

Note that since complex addition is commutative you may wish to implement __radd__ as well.

torek
  • 448,244
  • 59
  • 642
  • 775
-1

What's the problem here?

You can always check the type of a python object:

if type(other) != type(self):
    # raise some error
# do addition
return 
Ahmed
  • 120
  • 6