Andreas K.
Andreas K.

Reputation: 399

List comprehension of tuple gets me a generator - why and how to modify?

I am going through the exercises of this site https://anandology.com/python-practice-book/working-with-data.html when I tried to recreate the zip function through list comprehension. Now I created this function. But instead of getting a list I get a generator :-(

def zipp (liste1,liste2):
    length= len(liste1)
    zipped=[]
    [zipped.append(tuple(liste1[i], liste2[i]) for i in range(length))]
    return zipped

I searched a little in here and found this: Python: why does list comprehension produce a generator?

Accordingly I used the "tuple" statement already but to no awail.

I have no idea why I get a generator even with the tuple() inserted. So my questions:

  1. why?
  2. What do I need to change or where can I read/hear more to get "enlightened" myself?
  3. How could I use the generator to get the result? (or where can I read about this?)

Thanks.

edit: The result I expect is list of tuple with member of each list in it. This I what I should get:

zipp([1, 2, 3], ["a", "b", "c"]) -> [(1, "a"), (2, "b"), (3, "c")]

Upvotes: 2

Views: 425

Answers (2)

Wolphyrus Imperius
Wolphyrus Imperius

Reputation: 868

trying to answer your questions...

1.- why?

A: As many have mention, you return the zipped list as a list generators within the list comprehension [zipped.append(tuple(liste1[i], liste2[i]) for i in range(length))], not a nice way of doing by the way. that's why you get those generators.

2.- What do I need to change or where can I read/hear more to get "enlightened" myself?

A: If you still want to do that way, you only have to append the two items, by moving the parenthesis and removing the tuple function, like so:

def zipp (liste1, liste2):
    length = len(liste1)
    zipped = []
    [zipped.append( (liste1[i], liste2[i]) ) for i in range(length) ]  # not the best way, but still works. This created list is never used.
    return zipped

then it's possible to return the list

zipp([1,2,3], ['a','b','c'])

Note that this asumes both list have same length. Otherwise you have to choose one (like you are doing) or finding the minimum of both lengths (it's also posible to choose the longest and fill with whatever it's needed):

min(len(liste1), len(liste2))

3.- How could I use the generator to get the result?

A: For it to be a generator you need to yield the nedeed value:

def zipp2 (liste1,liste2):
    i = 0
    minval = min(len(liste1), len(liste2))
    while i< minval:
        yield (liste1[i], liste2[i])
        i += 1

# call the function generator
gen = zipp2([1,2,3], ['a','b','c'])
print(gen)
for p in gen:
    print(p)

and get the results...

<generator object zipp2 at 0x7fe46bef3db0>
(1, 'a')
(2, 'b')
(3, 'c')

Upvotes: 1

Jean-Fran&#231;ois Fabre
Jean-Fran&#231;ois Fabre

Reputation: 140178

you're putting a generator in your object:

tuple(liste1[i], liste2[i]) for i in range(length)

(and tuple doesn't work too, just remove it....)

(and don't use comprehensions for side effects)

The best way would be to rewrite it completely using a list comprehension which actually returns something, as list comprehensions are supposed to, taking the min of both lengths to fully emulate zip:

def zipp (liste1,liste2):
    return [(liste1[i], liste2[i]) for i in range(min(len(liste1),len(liste2)))]

classic loop version (no comprehensions)

def zipp (liste1,liste2):
   result = []
   for i in range(min(len(liste1),len(liste2))):
       result.append((liste1[i], liste2[i]))
   return result

of course this is nothing else than list(zip(liste1,liste2)) (forcing iteration on zip)

Upvotes: 2

Related Questions