
On 30 June 2013 14:53, Andrew Barnert <abarnert@yahoo.com> wrote:
If you don't raise an exception through a listcomp, that cost is basically running one more opcode and loading a few more bytes into memory. It adds less than 1% for even a trivial comp that loops 10 times, or for a realistic but still simple comp that loops 3 times.
Comprehensions translate to inline loops. The fact that the "stop()" hack works for generator expressions is just a quirky result of the line between "return" and "raise StopIteration" in a generator function being extraordinarily blurry - it has nothing to with breaking out of a loop. That's why the stop() hack terminates a nested genexp completely, rather than just breaking out of the innermost loop:
def stop(): ... raise StopIteration ... list((x, y) for x in range(10) for y in range(10) if y < 3 or stop()) [(0, 0), (0, 1), (0, 2)]
def greturn(): ... for x in range(10): ... for y in range(10): ... if y >= 3: return ... yield x, y ... list(greturn()) [(0, 0), (0, 1), (0, 2)]
def graise(): ... for x in range(10): ... for y in range(10): ... if y >= 3: raise StopIteration ... yield x, y ... list(graise()) [(0, 0), (0, 1), (0, 2)]
Note how all three produce the same output, and how both loops terminate immediately when the return/raise is encountered. You can't get the same semantics for other comprehensions without introducing the yield/return distinction, which is astonishingly slow by comparison (as you need to suspend and resume the generator frame on each iteration). The overhead of that is only worth it when the cost of having the entire result in memory at the same time is prohibitive. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia