HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonModerate

What's the optimal 'pythonic' way to make dot product of two lists of numbers?

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
pythonictheoptimalwhatnumbersmakewaydotproducttwo

Problem

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.67

Solution

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:

>>> x = [1, 4, 8]
>>> sum(x)
13
>>> y = [6, 1, 0]
>>> zip(x, y)

>>> list(_) # `_` means previous value
[(1, 6), (4, 1), (8, 0)]
>>> help(zip)
Help on class zip in module builtins:

class zip(object)
 |  zip(iter1 [,iter2 [...]]) --> 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.

>>> help(sum)
Help on built-in function sum in module builtins:

sum(...)
    sum(iterable[, start]) -> 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.

>>> zip( (1,2,3), "abc" )

>>> 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.

Code Snippets

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))
>>> x = [1, 4, 8]
>>> sum(x)
13
>>> y = [6, 1, 0]
>>> zip(x, y)
<zip object at 0x7f51da3db5c8>
>>> list(_) # `_` means previous value
[(1, 6), (4, 1), (8, 0)]
>>> help(zip)
Help on class zip in module builtins:

class zip(object)
 |  zip(iter1 [,iter2 [...]]) --> 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.

>>> help(sum)
Help on built-in function sum in module builtins:

sum(...)
    sum(iterable[, start]) -> 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.

>>> zip( (1,2,3), "abc" )
<zip object at 0x7f51dba79648>
>>> list(_)
[(1, 'a'), (2, 'b'), (3, 'c')]

Context

StackExchange Code Review Q#143203, answer score: 14

Revisions (0)

No revisions yet.