Re-iterable generator comprehensions
Oren Tirosh
oren-py-l at hishome.net
Sat Feb 2 12:34:35 EST 2002
On Mon, Apr 1, 2002 at 09:53:15AM -2600, J. Random Newbie wrote:
> I am using Python 2.3b1 and I am trying to use generator comprehensions
> to generate all combinations of a letter and a digit without creating any
> intermediate lists:
>
> >>>from __future__ import generator_comprehensions
> >>>letters = [yield chr(i) for i in xrange(chr('a'),chr('z')+1)]
> >>>digits = [yield str(i) for i in xrange(10)]
> >>>letdig = [yield l+d for l in letters for d in digits]
>
> The generator letdig yields 'a0' through 'a9' and stops. If I remove
> the yield keywords and use lists it works fine. What's happening here?
According to the current proposal for generator comprehensions the letters
and digits objects are iterators. They can only be traversed once. If you
call iter(digits) it just returns itself. When the letdig generator
reaches the end of the digits source it calls iter(digits) again, expecting
a fresh iterator for the same container. Instead it just gets the same
exhausted iterator and stops.
The solution is simple: generator comprehensions should not return an
iterator - they should return an iterable object. This will make their
behavior much more like real containers and make the code above work
as expected.
According to the current proposal for generator comprehensions the
letters object is equivalent to this code:
def tmpgen():
for i in xrange(chr('a'),chr('z')+1)]: yield chr(i)
letters = tmpgen()
With re-iterable generators it would be something like this:
def tmpgen():
for i in xrange(chr('a'),chr('z')+1)]: yield chr(i)
letters = ReGenerator(tmpgen)
where the class ReGenerator is defined as:
class ReGenerator:
def __init__(self, genfunc):
self.__iter__ = genfunc
This object more closely emulates a list or other container: each time
iter(letters) is called it returns a fresh, independent iterator. Unless
the generator or any of its sources are affected by some global variables
it should produce the same stream of objects again and again.
The should also apply to the functions xmap, xfilter and xzip. They
should return an iterable object, not an iterator. The xrange built-in
function already works this way: it returns an iterable xrange object
emulating a container. Each time iter(<xrange object>) is called it
returns a fresh, independent iterator.
from-__future__-ly yours,
Oren Tirosh
More information about the Python-list
mailing list