What's the optimal 'pythonic' way to make dot product of two lists of numbers? I just filled in the first assignment on course which involves learning Python. The assignment was to make vector class which supports scalar multiplication through operator overloading.

The instructions included these constains:


  
  There will be one class called MyVector
  Nothing will be imported in file within that file which contains the class
  The class will accept one argument in constructor, which must be list of numbers
  The class will have two methods:
  
  
  get_vector which returns list of numbers of the vector
  __mul__ (overloaded * operator) which performs scalar multiplication of two vectors
  
  


This is what I have sent:

class MyVector:
    """Vector class"""
    coordinates = None
    def __init__(self, coordinates):
        if not isinstance(coordinates, list):
            raise TypeError("Coordinates of vector must me single dimensional array.")
        self.coordinates = coordinates
    def f(self):
        return 'hello world'
    def get_vector(self):
        return self.coordinates
    def dimensions(self):
        return len(self.coordinates)
    def __mul__(self,other):
        if other.dimensions() != self.dimensions():
            raise ValueError("Number of dimensions of multiplied vectors must be equal.")
        tmp = 0
        for index in range(self.dimensions()):
            tmp += self.coordinates[index] * other.coordinates[index]
        return tmp

''' Just a testing section recommended in the assignment '''
if __name__ == "__main__":
    vec1 = MyVector([1,2,5,5,5]) # vektory mohou byt i jine dimenze nez 3!
    vec2 = MyVector([1,2,5,5,5]) 
    print(vec1.get_vector()) # Test getting the list of items
    dot_product = vec1*vec2  # Multiplication test
    print(dot_product)


The homework was OK, but the validation system is bragging that their implementation is faster:


  Message: module file vectors.py found
  Result ok
  Your elapsed time for 10,000 trials, vectors length 300: 0.675 seconds
  Our elapsed time for the same setting: 0.383 seconds  
  
  Points: 2 out of 2  


I'll repeat the relevant section of code here:

    def __mul__(self,other):
        if other.dimensions() != self.dimensions():
            raise ValueError("Number of dimensions of multiplied vectors must be equal.")
        tmp = 0
        for index in range(self.dimensions()):
            tmp += self.coordinates[index] * other.coordinates[index]
        return tmp


As you can see, as a seasoned C++ programmer I took naïve approach with indexed for loop. I know that Python has some famous tools to deal with array operation.

Is there some really pythonic way to loop over those two arrays and multiply their elements? Note that primarily I'm looking for pythonic way (since I'm learning Python), optimalization is secondary. I got my 2 points for homework, I'm not submiting it again.
The most "pythonic" way of doing this makes use of the sum and zip built-ins:

def __mul__(self,other):
    if other.dimensions() != self.dimensions():
        raise ValueError("Number of dimensions of multiplied vectors must be equal.")
    return sum(a * b for a, b in zip(self.coordinates, other.coordinates))


I suggest making use of the REPL (interactive shell) to learn about them, here is an example REPL session that you can read explaining zip and sum:

&gt;&gt;&gt; x = [1, 4, 8]
&gt;&gt;&gt; sum(x)
13
&gt;&gt;&gt; y = [6, 1, 0]
&gt;&gt;&gt; zip(x, y)
&lt;zip object at 0x7f51da3db5c8&gt;
&gt;&gt;&gt; list(_) # `_` means previous value
[(1, 6), (4, 1), (8, 0)]
&gt;&gt;&gt; help(zip)
Help on class zip in module builtins:

class zip(object)
 |  zip(iter1 [,iter2 [...]]) --&gt; zip object
 |  
 |  Return a zip object whose .__next__() method returns a tuple where
 |  the i-th element comes from the i-th iterable argument.  The .__next__()
 |  method continues until the shortest iterable in the argument sequence
 |  is exhausted and then it raises StopIteration.
 |  
 |  Methods defined here:
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __iter__(self, /)
 |      Implement iter(self).
 |  
 |  __new__(*args, **kwargs) from builtins.type
 |      Create and return a new object.  See help(type) for accurate signature.
 |  
 |  __next__(self, /)
 |      Implement next(self).
 |  
 |  __reduce__(...)
 |      Return state information for pickling.

&gt;&gt;&gt; help(sum)
Help on built-in function sum in module builtins:

sum(...)
    sum(iterable[, start]) -&gt; value

    Return the sum of an iterable of numbers (NOT strings) plus the value
    of parameter 'start' (which defaults to 0).  When the iterable is
    empty, return start.

&gt;&gt;&gt; zip( (1,2,3), "abc" )
&lt;zip object at 0x7f51dba79648&gt;
&gt;&gt;&gt; list(_)
[(1, 'a'), (2, 'b'), (3, 'c')]


Other points


Tests should be automatic and NOT rely on you manually checking that wha is printed is right. This manual method will become too time consuming very soon.
f makes no sense (probably it is a leftover from a previous assignment). Please remove it.
get methods are discouraged in Python, accessing the items directly is very simple anyway.
Use @property to give convenient call sintax in dimensions(self) or remove this property completely as you can just call len(vector.coordinates) very easily.