Alanlyyy
Alanlyyy

Reputation: 67

How to return a copy of an instance of a class?

I am currently practising python on code wars, here is a prompt:

Create a Vector object that supports addition, subtraction, dot products, and norms. So, for example:

    a = Vector([1, 2, 3])
    b = Vector([3, 4, 5])
    c = Vector([5, 6, 7, 8])

    a.add(b)      # should return a new Vector([4, 6, 8])
    a.subtract(b) # should return a new Vector([-2, -2, -2])
    a.dot(b)      # should return 1*3 + 2*4 + 3*5 = 26
    a.norm()      # should return sqrt(1^2 + 2^2 + 3^2) = sqrt(14)
    a.add(c)      # raises an exception

I have written functions add and subtract that pass some of the tests. However, I am running into issues with overwriting my previous list values of 'a' after running the add function. When I go into subtract, the 'a' values in the vector are the summations computed from the previous instance of the add function.

I suspect its due to me running this line of code: return self.__class__(self.list) causing the instance of the class to overwrite itself.

Kindly please help, I believe I need to return a copy of the instance of the class but don't know how to do it.

    class Vector:

      def __init__(self, list):

          self.list = list #[1,2]
          self.copylist = list

      def add(self,Vector):

          try:
              self.list = self.copylist

              #take list from other vector
              other = Vector.list

              #take each value from other Vector and add it to self.list
              for index,item in enumerate(Vector.list,0):
                  self.list[index] = item + self.list[index]


          except:
              print("Different size vectors")
          #return the instance of a class
          return self.__class__(self.list)

       def subtract(self,Vector):

          self.list = self.copylist
          other = Vector.list

          print(self.list)
          print(other)

          for index,item in enumerate(Vector.list,0):
              self.list[index] = self.list[index] - item

          return self.__class__(self.list)

       def dot(self,Vector):
          self.list = self.copylist
          other = Vector.list

          #print(self.list)
          #print(other)

          running_sum =0

          for index,item in enumerate(Vector.list,0):
              running_sum = running_sum + item * self.list[index]
              #print(running_sum, " ", self.list[index], " ", item)

          return running_sum

       def norm(self):
          running_sum = 0

          for item in self.list:
              running_sum += item**2

          return running_sum ** 0.5

       def toString(self):
          return str(self.list)

      `def equals(self,Vector):
          return self.list == Vector.list

Here are some of the tests:

    a = Vector([1, 2])
    b = Vector([3, 4])

    test.expect(a.add(b).equals(Vector([4, 6])))


    a = Vector([1, 2, 3])
    b = Vector([3, 4, 5])

    test.expect(a.add(b).equals(Vector([4, 6, 8])))
    test.expect(a.subtract(b).equals(Vector([-2, -2, -2]))) #code fails here
    test.assert_equals(a.dot(b), 26)
    test.assert_equals(a.norm(), 14 ** 0.5)

Upvotes: 2

Views: 1513

Answers (3)

Maxneto CH kk
Maxneto CH kk

Reputation: 1

Please change function toString to str . its' already done.

class Vector : 
    def __init__(self , lst_vec):
        self.lst_vec = lst_vec

    def show_vector(self):
        return self.lst_vec

    def add(self , v ):
        size_self = len(self.lst_vec)
        size_v = len(v.lst_vec)
        new_vector = []
        if ( size_self != size_v ):
            return Exception("error add")
        else:
            for i in range(size_self):
                new_vector.append(self.lst_vec[i] + v.lst_vec[i])
        return Vector(new_vector)

    def subtract(self , v ): 
        size_self = len(self.lst_vec)
        size_v = len(v.lst_vec)
        new_vector = []
        if ( size_self != size_v ):
            return Exception("error subtract")
        else:
            for i in range(size_self):
                new_vector.append(self.lst_vec[i] - v.lst_vec[i])
        return Vector(new_vector)

    def dot(self , v ):
        size_self = len(self.lst_vec)
        size_v = len(v.lst_vec)
        new_vector = []
        sum_vec = 0 
        if ( size_self != size_v ):
            return Exception("Vector sizes are different")
        else:
            for i in range(size_self):
                new_vector.append(self.lst_vec[i] * v.lst_vec[i])
        for i in range(len(new_vector)):
            sum_vec+=new_vector[i]
        return sum_vec

    def norm (self):
        new_vec_sum = 0
        for i in range(len(self.lst_vec)):
            new_vec_sum +=( self.lst_vec[i] ) **2
        return new_vec_sum ** 0.5

    def toString(self):
        str_self = '('
        for i in range(len(self.lst_vec)):
            str_self += str(self.lst_vec[i])
            if i < (len(self.lst_vec)-1):
                str_self+=','
            else : pass 
        str_self+=')'
        return str_self

    def equals(self , v ):
        return self.lst_vec == v.lst_vec 

a = Vector([1,2,3])
b = Vector([3,4,5])
c = Vector([5,6,7,8])
print(a.add(b).show_vector())
print( a.add(b).equals(Vector([4,6,8])) )
print(a.subtract(b).show_vector())
print(a.dot(b))
print(a.norm())
print((a.toString() == '(1,2,3)'))
print(c.toString())

Upvotes: 0

CryptoFool
CryptoFool

Reputation: 23119

I think you're making this more complicated than it needs to be. You shouldn't be working with class objects at all. You should just be working with instances of the Vector class. Here's what I think your code should look like:

class Vector:

    def __init__(self, initial_elements):

        self.elements = list(initial_elements) # make a copy of the incoming list of elements

    def add(self, other):

        # insure that the two vectors match in length
        if len(self.elements) != len(other.elements):
            raise Exception("Vector sizes are different")

        # copy our elements
        r = list(self.elements)

        # add the elements from the second vector
        for index, item in enumerate(other.elements, 0):
            r[index] += item

        # return a new vector object defined by the computed elements
        return Vector(r)

    def subtract(self, other):

        # insure that the two vectors match in length
        if len(self.elements) != len(other.elements):
            raise Exception("Vector sizes are different")

        # copy our elements
        r = list(self.elements)

        # subtract the elements from the second vector
        for index, item in enumerate(other.elements, 0):
            r[index] -= item

        # return a new vector object defined by the computed elements
        return Vector(r)

    def dot(self, other):

        running_sum = 0

        for index, item in enumerate(other.elements, 0):
            running_sum += item * self.elements[index]

        return running_sum

    def norm(self):
        running_sum = 0

        for item in self.elements:
            running_sum += item ** 2

        return running_sum ** 0.5

    def toString(self):
        return str(self.elements)

    def equals(self, other):
        return self.elements == other.elements

def test():
    a = Vector([1, 2])
    b = Vector([3, 4])
    print(a.add(b).equals(Vector([4, 6])))

    a = Vector([1, 2, 3])
    b = Vector([3, 4, 5])

    print(a.add(b).equals(Vector([4, 6, 8])))
    print(a.subtract(b).equals(Vector([-2, -2, -2])))
    print(a.dot(b) == 26)
    print(a.norm() == 14 ** 0.5)

test()

Result:

True
True
True
True
True

The general structure of your code is spot on.

One thing to note is that you shouldn't be using list as a variable name, as it is a type name in Python. Also, you don't want to be passing around Vector as a value. You want to be passing instances of Vector and list, with names that do not conflict with these type names.

My solution assumes you want Vector instances to be immutable, so each of your operations will return a new Vector object. You could also have them not be immutable and have, for example, the add method just add the incoming vector into the target vector without creating a new object. I like keeping them immutable. I've been doing more and more of this "functional style" programming lately, where calls to object methods don't modify the target object (don't have side effects), but rather just return a new object.

I like your use of the test class to do your testing. I chose to not deal with this, and just print the results of each test comparison to see that they all come out to True. I'll leave it to you to restore your tests to using a test object with expect and assert_equals methods.

UPDATE: Here is a more compact way to write your add and subtract methods:

def add(self, other):

    # insure that the two vectors match in length
    if len(self.elements) != len(other.elements):
        raise Exception("Vector sizes are different")

    return Vector([self.elements[i] + other.elements[i] for i in range(len(self.elements))])

def subtract(self, other):

    # insure that the two vectors match in length
    if len(self.elements) != len(other.elements):
        raise Exception("Vector sizes are different")

    return Vector([self.elements[i] - other.elements[i] for i in range(len(self.elements))])

Upvotes: 3

Kenivia
Kenivia

Reputation: 384

change:

return self.__class__(self.list)

to:

return self

although this would the same as,

return Vector(self.list)

if the class is more complicated it is better to return self

I think that's the issue, hope it helps :)

also, it is good practice to use different names. you used Vector for the class name as well as many of the inputs of the functions, you will run into problems when you do that.

Upvotes: 0

Related Questions