![](https://secure.gravatar.com/avatar/664d320baa05c827ff08ed361fe77769.jpg?s=120&d=mm&r=g)
On 30 June 2013 00:51, Nick Coghlan <ncoghlan@gmail.com> wrote:
[x for x in iterable; break if x is None] [x for x in data if x; break if x is None]
One nice advantage of that notation is that:
1. The statement after the ";" is exactly the statement that would appear in the expanded loop 2. It can be combined unambiguously with a filtering clause 3. It clearly disallows its use with nested loops in the comprehension
It has the significant disadvantage that Steven pointed out which is that it doesn't read very well. The most important aspect of a comprehension is its comprehensibility. Consider getting the prime numbers less than 100: primes100 = {p for p in primes(); break if p >= 100} You need to invert the if condition to understand which primes are in the resulting set. With for/while it reads properly and the condition at the right expresses a true property of the elements in the resulting set: primes100 = {p for p in primes() while p < 100} At the moment the obvious way to get the prime numbers less than 100 would be to do something like: from math import ceil, sqrt def isprime(N): return N > 1 and all(N % n for n in range(2, ceil(sqrt(N)))) primes100 = [p for p in range(1, 100) if isprime(p)] However this is a suboptimal algorithm. At the point when we want to determine if the number N is prime we have already found all the primes less than N. We only need to check modulo division against those but this construction doesn't give us an easy way to do that. It's better to have a primes() generator that can keep track of this information: from itertools import count def primes(): primes_seen = [] for n in count(2): if all(n % p for p in primes_seen): yield n primes_seen.append(n) This algorithm is actually even poorer as it doesn't stop at sqrt(n). We can fix that with takewhile: from itertools import count, takewhile def primes(): primes_seen = [] for n in count(2): if all(n % p for p in takewhile(lambda p: p**2 < n, primes_seen)): yield n primes_seen.append(n) primes100 = {p for p in takewhile(lambda p: p < 100, primes()} Using for/while this becomes significantly clearer (in my opinion): from itertools import count def primes(): primes_seen = [] for n in count(2): if all(n % p for p in primes_seen while p**2 <= n): yield n primes_seen.append(n) primes100 = {p for p in primes() while p < 100} The main objection to for/while seems to be that it doesn't unroll in the same way as current comprehensions. I think that for/while is just as useful for an ordinary for loop as it is for a comprehension. In C you can easily add anything to the termination condition for a loop e.g.: for (i=0; i<N && i && p[i-1]<100; ++i) p[i] = next_prime(); But in Python having multiple termination conditions (or having any termination with an inifinite iterator) means using a break or takewhile/lambda. for n, p in enumerate(primes()): if p > 100: break print('%sth prime is %s' % (n, p)) or perhaps for n, p in enumerate(takewhile(lambda p: p < 100, primes())): print('%sth prime is %s' % (n, p)) or even worse for n, p in enumerate(takewhile((100).__gt__, primes())): print('%sth prime is %s' % (n, p)) I think that it would be better if this could be spelled as for n, p in enumerate(primes()) while p < 100: print('%sth prime is %s' % (n, p)) If that were the case then a for/while comprehension could unroll into a for/while loop just as with current comprehensions: result = [x for y in stuff while z] becomes: result = [] for y in stuff while z: result.append(x) Oscar