[Python-ideas] Is this PEP-able? for X in ListY while conditionZ:

Oscar Benjamin oscar.j.benjamin at gmail.com
Thu Jun 27 01:41:46 CEST 2013


On 27 June 2013 00:20, Nick Coghlan <ncoghlan at gmail.com> wrote:
>
> On 27 Jun 2013 08:57, "David Mertz" <mertz at gnosis.cx> wrote:
>>
>> On Wed, Jun 26, 2013 at 3:39 PM, Alexander Belopolsky
>> <alexander.belopolsky at gmail.com> wrote:
>>>
>>> I can do this with generators now:
>>>
>>> >>> def stop():
>>> ...    raise StopIteration
>>> ...
>>> >>> list(i for i in range(10) if i < 3 or stop())
>>> [0, 1, 2]
>>>
>>> Can't the same be allowed in compehensions?

For reference this was discussed 6 months ago in the thread starting here:
http://mail.python.org/pipermail/python-ideas/2013-January/018969.html

>> This will only work in generator comprehensions.  The reasons are obvious
>> if you think about it.  But it isn't too ugly as a technique, IMO.  I still
>> like the 'while' clause in generators (notwithstanding the fact it doesn't
>> translate straightforwardly to an "unrolled block"), but this is sort of
>> nice (and maybe I'll start using it):
>>
>> >>> dict((i,i*2) for i in range(10) if i < 3 or stop())
>> {0: 0, 1: 2, 2: 4}
>
> I'm personally not opposed to allowing "else break" after the if clause as a
> way of terminating iteration early in comprehensions and generator
> expressions, as it's consistent with the existing translation to an explicit
> loop:
>
> {i:i*2 for i in range(10) if i<3 else break}

If I remember there was an objection to using "else break" in
preference for "else return" as it was deemed (by some) that it wasn't
clear which loop the break applied to in the case where there were
nested loops. The "else return" version still makes sense when
unrolled if the intention is to terminate all the loops.

> I'd even be OK with the use of an "else" clause to define alternative
> entries to use when the condition is false:
>
> {i:i*2 for i in range(10) if i<3 else i:i}

You can use ternary if/else in the expression rather than the if clause:

{i:i*2 if i < 3 else i for i in range(10)}

I think that's clearer in this case. This always works for list/set
comprehensions and for generators. For dict comprehensions it works
but it's less good if you need to use it for both key and value, i.e.

{i*2: i*3 for i in range(10) if i < 3 else i:i}

would become

{i*2 if i < 3 else i: i*3 if i < 3 else i for i in range(10)}

> A reasonable constraint on the complexity may be to disallow mixing this
> extended conditional form with the nested loop form.

I would welcome an addition along these lines with that constraint. I
rarely use nested loops in comprehensions and often find myself
writing simple while loops in situations that would be covered by a
comprehension that used "else break".


Oscar


More information about the Python-ideas mailing list