Search code examples
python-3.xadditionreversedatamodel

Python 3 method to flip arguments in an addition operation


I have a class that adds two points (one of which is of type Point, and the other is a tuple or list -- see code). My problem is that my add method will only work if I input the numbers in a certain order. I need to create a second method (per the rules in this assignment) that only contains one line calling the add method and returns the result and can supposedly be found in the data model of the documentation.

class Point():
def __init__(self, x=0, y=0):
    self.x = x
    self.y = y

def __str__(self):
    return ("X = " + str(self.x) + "\nY = " + str(self.y))

def __add__(self, other):
    if isinstance(other,list) or isinstance(other,tuple):
        newX = other[0] + self.x
        newY = other[1] + self.y
        return(Point(newX,newY))
    else:
        newX = self.x + other
        newY = self.y + other
        return(Point(newX,newY))

p = Point(5,10)
print(p + [3.5,6])
print([3.5,6] + p)

I've scoured the data model and I can only think that reversed or something with getattr would work, but I have no idea how to implement either, or if I'm even on the right track. Please help!


Solution

  • You should add the __radd__() in your class:

    def __radd__(self, *args, **kwargs):
        return self.__add__(*args, **kwargs)
    

    This will actually do an __add__() as in this case it really is the same.

    but you may consider making the class behave like a list, something like this:

    class Point():
        def __init__(self, x=0, y=0):
            self.x = x
            self.y = y
            self._list = [self.x, self.y]
    
        def __str__(self):
            return ("X = " + str(self.x) + "\nY = " + str(self.y))
    
        def __repr__(self):
            return str(self._list)
    
        def __iter__(self):
            for elem in self._list:
                yield elem
    
        def __getitem__(self, i):
            return self._list(i)
    
        def __len__(self):
            return len(self._list)
    
        def __delitem__(self, i):
            #not sure if you really want this
            del self._list[i]
    
        def __setitem__(self, i, val):
            #not sure if you really want this
            self._list[i] = val    
    
        def __add__(self, other):
            #this code could be optimized
            #using your original code for this example
            if isinstance(other,list) or isinstance(other,tuple):
                newX = other[0] + self.x
                newY = other[1] + self.y
            else:
                newX = self.x + other
                newY = self.y + other
            #returning a list
            return [newX, newY]
    
        def __radd__(self, *args, **kwargs):
            return self.__add__(*args, **kwargs)
    
    
    p = Point(5,10)
    print(p)
    print(p + [3.5,6])
    print([3.5,6] + p)
    

    output:

    [5, 10]
    [8.5, 16]
    [8.5, 16]
    

    [edit] If you do decide to make it behave like a list, you might as well subclass list, example:

    class Point(list):
    
        def __init__(self, x=0, y=0):
            super(Point, self).__init__([x, y])
    
        def __add__(self, other):
            if isinstance(other, (list, tuple)):
                return [sum(i) for i in zip(self, other)]
            elif isinstance(other, (int, float)):
                return [i + other for i in self]
            else:
                return self
    
        def __radd__(self, *args, **kwargs):
            return self.__add__(*args, **kwargs)
    
        #you probably want a __sub__
        def __sub__(self, other):
            if isinstance(other, (list, tuple)):
                return [i - j for i, j in zip(self, other)]
            elif isinstance(other, (int, float)):
                return [i - other for i in self]
            else:
                return self
    
        #and an __rsub__
        def __rsub__(self, other):
            if isinstance(other, (list, tuple)):
                return [i - j for i, j in zip(other, self)]
            elif isinstance(other, (int, float)):
                return [other - i for i in self]
            else:
                return self   
    
        #take away functions you do not want
        def pop(*args, **kwargs): pass
        def sort(*args, **kwargs): pass
        def append(*args, **kwargs): pass
        def extend(*args, **kwargs): pass
        def insert(*args, **kwargs): pass
        def remove(*args, **kwargs): pass
        def reverse(*args, **kwargs): pass
    
    p = Point(5,10)
    tests = [[3.5, 6], (5,4), 1.1, 'wrong string', [1, 2, 3]]
    for test in tests:
        print(p + test)
        print(test + p)
    for test in tests:
        print(p - test)
        print(test - p)   
    p.append(4)
    print(p)
    

    output:

    [8.5, 16]
    [8.5, 16]
    [10, 14]
    [10, 14]
    [6.1, 11.1]
    [6.1, 11.1]
    [5, 10]
    [5, 10]
    [6, 12]
    [6, 12]
    [1.5, 4]
    [-1.5, -4]
    [0, 6]
    [0, -6]
    [3.9, 8.9]
    [-3.9, -8.9]
    [5, 10]
    [5, 10]
    [4, 8]
    [-4, -8]
    [5, 10]