[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

What does <generator object <generator object <genexpr> at 0x0402C7B0> mean?

On 21May2019 18:11, CrazyVideoGamez <jasonanyilian at gmail.com> wrote:
>I tried doing a list comprehension. I typed:
>favorite_fruits = ['watermelon', 'blackberries']
>print(fruit for fruit in favorite_fruits)
>And I got:
><generator object <genexpr> at 0x0402C7B0>
>What does this mean and what do I have to fix?

It means you didn't make a list comprehension. You've discovered a 
similar thing though.

A list comprehension includes square brackets:

  [ fruit for fruit in favorite_fruits ]

The result of that expression is literally a list, in this case 
effectively a copy of your favorite_fruits list.

However, what you wrote was this:

  fruit for fruit in favorite_fruits

and that is a generator expression. It is effectively an object that you 
can iterate over, and it will yield the elements from favourite_fruits.

Importantly, it doesn't construct a list. It constructs a tiny function.

Why might we want such a thing? Because it is "lazy": it only computes 
values which are actually asked for (consumed). The list comprehension 
computes all the values, _and_ allocates a list and stores them! This 
consumes more memory (except for small lists) and stalls your programme 
while that work takes place.

It is equivalent to this function:

  def elements_of(some_list):
    for element in some_list:
      yield element

That is a "generator function", and when you call it eg 
"elements_of(favorite_fruits)" it doesn't run. Instead it returns you a 
generator object which is effectively a call to the function frozen in 

The generator object is iterable, and when you iterate over it if fires 
up the frozen function. The function runs until it hits a yield 
statement, where it yields a value and that is the next value for the 
iteration; the function freezes again then.

Consider this loop:

  stop_at = 'watermelon'
  for value in [ fruit for fruit in favorite_fruits ]:
    if value == stop_at:

This (a) constructs a list containing all the elements of 
favorite_fruits then (b) iterates over the elements of that list. When 
it hits a particular one of those elements it exits the loop.

If we unfold the "for" line a little:

  elements = [ fruit for fruit in favorite_fruits ]
  for value in elements:

The same amount of work is done, it just makes clear that the list 
construction is a whole distinct step.

Now compare (generator expression in brackets, for clarity.:

  stop_at = 'watermelon'
  elements = fruit for fruit in favorite_fruits
  for value in elements:
    if value == stop_at:

Now "elements" is not a list, it is a generator expression. You can 
still iterate over this, and the printed output will be the same.  
However, before the "for", _no_ elements of favorite_fruits have been 
iterated over - almost no work has happened at all. Then as the for loop 
iterates, it fetches values from "elements", which yields them as 

So that when the loop stops early, no effort (or memory storage) is 
wasted looking at the tail of the list.

Your list is very small, but for large lists this is a significant win.  
Some lists are (notionally) infinitely large.

Cameron Simpson <cs at cskk.id.au>