On Tue, Jan 29, 2013 at 10:35 PM, Joao S. O. Bueno jsbueno@python.org.br wrote:
On 29 January 2013 09:51, yoav glazner yoavglazner@gmail.com wrote:
Here is very similar version that works (tested on python27)
def stop():
next(iter([]))
list((i if i<50 else stop()) for i in range(100))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49]
Great. I think this nails it. It is exactly the intended behavior, and very readable under current language capabilities.
One does not have to stop and go read what "itertools.takewhile" does, and mentally unfold the lambda guard expression - that is what makes this (and the O.P. request) more readable than using takewhile.
Note: stop can also just explictly raise StopIteration - or your next(iter([])) expression can be inlined within the generator.
It works in Python 3 as well - though for those who did not test: it won't work for list, dicr or set comprehensions - just for generator expressions.
This actually prompted an interesting thought for me. The statement-as-expression syntactic equivalent of the "else stop()" construct would actually be "else return", rather than "else break", since the goal is to say "we're done", regardless of the level of loop nesting.
It just so happens that, inside a generator (or generator expression) raising StopIteration and returning from the generator are very close to being equivalent operations, which is why the "else stop()" trick works. In a 3.x container comprehension, the inner scope is an ordinary function, so the equivalence between returning from the function and raising StopIteration is lost.
Cheers, Nick.