Thomas N. Thomas N. - 1 month ago 6
Python Question

Python - What am I doing wrong with my generators?

I'm trying to yield a series of generators to another generator via helper functions. I guess I'm trying to mimic

flat_map()
like in RxPy. Is there a way to accomplish this? I am getting an output that is printing the generators, not the items yielded by the generators.

class User(object):
def __init__(self, id, name):
self.id = id
self.name = name

def __str__(self):
return "{0}-{1}".format(self.id, self.name)

users = [
User(0,"Hero"),
User(1,"Dunn"),
User(2,"Sue"),
User(3,"Chi"),
User(4,"Thor"),
User(5,"Clive"),
User(6,"Hicks"),
User(7,"Devin"),
User(8,"Kate"),
User(9,"Klein"),
]

friendships = [
(0,1),
(0,2),
(1,2),
(1,3),
(2,3),
(3,4),
(4,5),
(5,6),
(5,7),
(6,8),
(7,8),
(8,9)
]

def user_for_id(user_id):
for user in users:
if user.id == user_id:
yield user



def friends_of(user):
for friendship in friendships:
if friendship[0] == user.id or friendship[1] == user.id:
for other_user_id in friendship:
if other_user_id != user.id:
yield user_for_id(other_user_id)

for friend in friends_of(users[3]):
print(friend)


OUTPUT:

<generator object user_for_id at 0x7f2d33ee48e0>
<generator object user_for_id at 0x7f2d33ee4990>
<generator object user_for_id at 0x7f2d33ee4938>

Answer

Your chaining generators together and only iterating over one of them. If you want to get the actually friends, you'll need to iterate over friend, even if it's a single value. Generators are NOT iterators. Because your friends_of function returns a generator expression, you either need to iterate over it or pass it to next.

>>> for friend in friends_of(users[3]):
...     for obj in friend:
...             print(obj.name)
...
Dunn
Sue
Thor

You can get around the second loop by passing the generator to next() and printing the resulting name property.

>>> for friend in friends_of(users[3]):
...     print(next(friend).name)
...
Dunn
Sue
Thor

If you've having difficulty understanding generators I would highly suggest looking into the materials published on david beazley's website.

http://dabeaz.com/generators/index.html - Generator tricks for system programmers http://dabeaz.com/generators-uk/index.html - Generator tricks for system programmers v2 http://dabeaz.com/finalgenerator/index.html - Generators the final frontier

His videos and slides will give you a much better idea as to how generators work in python.

As somebody else explained, in Python 3 you can use "yield from":

def friends_of(user):
    for friendship in friendships:
        if friendship[0] == user.id or friendship[1] == user.id:
            for other_user_id in friendship:
                if other_user_id != user.id:
                    yield from user_for_id(other_user_id)

for friend in friends_of(users[3]):
    print(friend.name)
Comments