113

I'd like to know what happens when I pass the result of a generator function to python's enumerate(). Example:

def veryBigHello():
    i = 0
    while i < 10000000:
        i += 1
        yield "hello"

numbered = enumerate(veryBigHello())
for i, word in numbered:
    print i, word
    

Is the enumeration iterated lazily, or does it slurp everything into the <enumerate object> first? I'm 99.999% sure it's lazy, so can I treat it exactly the same as the generator function, or do I need to watch out for anything?

Adam
  • 2,110
  • 3
  • 14
  • 17

4 Answers4

130

It's lazy. It's fairly easy to prove that's the case:

>>> def abc():
...     letters = ['a','b','c']
...     for letter in letters:
...         print letter
...         yield letter
...
>>> numbered = enumerate(abc())
>>> for i, word in numbered:
...     print i, word
...
a
0 a
b
1 b
c
2 c
David Webb
  • 190,537
  • 57
  • 313
  • 299
  • 1
    Is this Python 2 or 3 (or both)? Is it lazy in both? I tested on Python 2 and it *is* lazy. – a06e Apr 21 '16 at 21:22
  • 4
    I tested this on Python 3.5.2, and it evaluates lazily. – gobernador Jan 22 '19 at 17:05
  • Great explanation, thanks. So basically there's no cost overhead to wrapping a generator in `enumerate` is the takeaway, right? It won't parse the object N times... – Adam Hughes Feb 15 '22 at 19:07
53

It's even easier to tell than either of the previous suggest:

$ python
Python 2.5.5 (r255:77872, Mar 15 2010, 00:43:13)
[GCC 4.3.4 20090804 (release) 1] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
>>> abc = (letter for letter in 'abc')
>>> abc
<generator object at 0x7ff29d8c>
>>> numbered = enumerate(abc)
>>> numbered
<enumerate object at 0x7ff29e2c>

If enumerate didn't perform lazy evaluation it would return [(0,'a'), (1,'b'), (2,'c')] or some (nearly) equivalent.

Of course, enumerate is really just a fancy generator:

def myenumerate(iterable):
   count = 0
   for _ in iterable:
      yield (count, _)
      count += 1

for i, val in myenumerate((letter for letter in 'abc')):
    print i, val
Wayne Werner
  • 49,299
  • 29
  • 200
  • 290
14

Since you can call this function without getting out of memory exceptions it definitly is lazy

def veryBigHello():
    i = 0
    while i < 1000000000000000000000000000:
        yield "hello"

numbered = enumerate(veryBigHello())
for i, word in numbered:
    print i, word
Nikolaus Gradwohl
  • 19,708
  • 3
  • 45
  • 61
0

Old school alternative since I was using a generator that someone else (sklearn) wrote that didn't work with the approaches here.

i=(-1)
for x in some_generator:
    i+=1
Kermit
  • 4,922
  • 4
  • 42
  • 74