Praxis
Praxis

Reputation: 964

How to correctly capture generator output in a list comprehension?

I'm having some confusion with regards to a generator that I am using to create a sum of squares in python. Essentially I am iterating over vectors stored in a dictionary and finding the SS with another target vector.

I'm trying to code more pythonically by using list comprehension and generators but am struggling to understand how this is meant to work. The function that 'generates' just returns a string

<generator object distance.<locals>.operation at 0x00000181748E2048>

Which is obviously wrong. Any advice would be appreciated. Ideally want to output the result of the SS operation but also interested in how I am misunderstanding this.

import math

v = [2,-2,1,3]
dv = {'a1':[0,1,3,5], 'a2':[2,5,6,7], 'a3':[1,-2,-3,2], 'a4':[2,2,1,1],
      'a5':[3,2,1,-1]}

def distance(v, dv):

    def operation(orig, nbr):
        yield [sum(math.sqrt(orig - nbr) for orig, nbr in zip(orig, nbr))]

    for k in sorted(dv):
        l = dv[k]
        list = operation(v,l)
        print(list)

distance(v, dv)

Upvotes: 2

Views: 813

Answers (3)

Alexander
Alexander

Reputation: 109526

You don't need yield as you are returning a list. The below sum function in operation is actually a generator expression than accomplishes the same thing.

Thus, sum([x ** 0.5 for x in some_list]) becomes sum(x for x in some_list) using a generator expression which uses less memory.

def distance(v, dv):

    def operation(orig, nbr):
        return sum(abs(orig - nbr) ** 0.5 for orig, nbr in zip(orig, nbr))

    return [operation(v, dv[l]) for l in sorted(dv)]

>>> distance(v, dv)
[5.974691494688162, 6.8818192885643805, 4.0, 3.414213562373095, 5.0]

Upvotes: 2

Rohit Jain
Rohit Jain

Reputation: 213223

You're using generators wrong way. That yield will anyways only be executed once (provided you convert the return value to a list). And it's result in not even used in a generator expression.

You can change that code to something like this:

def operation(orig, nbr):
    for orig, nbr in zip(orig, nbr):
        yield math.sqrt(abs(orig - nbr))

for k in sorted(dv):
    l = dv[k]
    value = sum(operation(v,l))
    print(value)

Now, this is using the generator function properly. Everytime the sum() function needs the next value, the for loop inside operation() function will be executed 1 step, and it will yield the next sqrt value, until it is exhausted.

Upvotes: 3

David Gomes
David Gomes

Reputation: 5825

You want to use list(operation(v, l)). Generators are yielded as a generator object and so you have to explicitly convert them to a list using list().

Explained with examples here.

Upvotes: 3

Related Questions