Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does enumerate() produce a generator object?

Tags:

python

As a complete Python newbie, it certainly looks that way. Running the following...

x = enumerate(['fee', 'fie', 'foe']) x.next() # Out[1]: (0, 'fee')  list(x) # Out[2]: [(1, 'fie'), (2, 'foe')]  list(x) # Out[3]: [] 

... I notice that: (a) x does have a next method, as seems to be required for generators, and (b) x can only be iterated over once, a characteristic of generators emphasized in this famous python-tag answer.

On the other hand, the two most highly-upvoted answers to this question about how to determine whether an object is a generator would seem to indicate that enumerate() does not return a generator.

import types import inspect  x = enumerate(['fee', 'fie', 'foe'])  isinstance(x, types.GeneratorType) # Out[4]: False  inspect.isgenerator(x) # Out[5]: False 

... while a third poorly-upvoted answer to that question would seem to indicate that enumerate() does in fact return a generator:

def isgenerator(iterable):     return hasattr(iterable,'__iter__') and not hasattr(iterable,'__len__')  isgenerator(x) # Out[8]: True 

So what's going on? Is x a generator or not? Is it in some sense "generator-like", but not an actual generator? Does Python's use of duck-typing mean that the test outlined in the final code block above is actually the best one?

Rather than continue to write down the possibilities running through my head, I'll just throw this out to those of you who will immediately know the answer.

like image 301
Josh O'Brien Avatar asked May 14 '14 19:05

Josh O'Brien


1 Answers

While the Python documentation says that enumerate is functionally equivalent to:

def enumerate(sequence, start=0):     n = start     for elem in sequence:         yield n, elem         n += 1 

The real enumerate function returns an iterator, but not an actual generator. You can see this if you call help(x) after doing creating an enumerate object:

>>> x = enumerate([1,2]) >>> help(x) class enumerate(object)  |  enumerate(iterable[, start]) -> iterator for index, value of iterable  |    |  Return an enumerate object.  iterable must be another object that supports  |  iteration.  The enumerate object yields pairs containing a count (from  |  start, which defaults to zero) and a value yielded by the iterable argument.  |  enumerate is useful for obtaining an indexed list:  |      (0, seq[0]), (1, seq[1]), (2, seq[2]), ...  |    |  Methods defined here:  |    |  __getattribute__(...)  |      x.__getattribute__('name') <==> x.name  |    |  __iter__(...)  |      x.__iter__() <==> iter(x)  |    |  next(...)  |      x.next() -> the next value, or raise StopIteration  |    |  ----------------------------------------------------------------------  |  Data and other attributes defined here:  |    |  __new__ = <built-in method __new__ of type object>  |      T.__new__(S, ...) -> a new object with type S, a subtype of T 

In Python, generators are basically a specific type of iterator that's implemented by using a yield to return data from a function. However, enumerate is actually implemented in C, not pure Python, so there's no yield involved. You can find the source here: http://hg.python.org/cpython/file/2.7/Objects/enumobject.c

like image 145
dano Avatar answered Sep 22 '22 23:09

dano