[Python-ideas] while conditional in list comprehension ??

Oscar Benjamin oscar.j.benjamin at gmail.com
Thu Jan 31 01:37:23 CET 2013


On 30 January 2013 22:56, Steven D'Aprano <steve at pearwood.info> wrote:
> On 31/01/13 05:05, Oscar Benjamin wrote:
>>
>> On 30 January 2013 17:56, Yuriy Taraday<yorik.sar at gmail.com>  wrote:
>>>
>>>
>>> On Wed, Jan 30, 2013 at 1:46 PM, Wolfgang Maier
>>> <wolfgang.maier at biologie.uni-freiburg.de>  wrote:
>>>>
>>>>
>>>> your condition is 'partial(lt,50)', but this is not met to begin with
>>>> and
>>>> results in an empty list at least for me. Have you two actually checked
>>>> the
>>>> output of the code or have you just timed it?
>>>
>>>
>>> Yeah. Shame on me. You're right. My belief in partial and operator module
>>> has been shaken.
>>>
>>
>> This is why I prefer this stop() idea to any of the takewhile()
>> versions: regardless of performance it leads to clearer code, that can
>> be understood more easily.
>
> Funny you say that, clarity of code and ease of understanding is exactly why
> I dislike this stop() idea.
>
>
> 1) It does not work with list, dict or set comprehensions, only with
> generator
>    expressions. So if you need a list, dict or set, you have to avoid the
>    obvious list/dict/set comprehension.

That's true. I would prefer it if a similar effect were achievable in
these cases.

>
> 2) It is fragile: it is easy enough to come up with examples of the above
>    that *appear* to work:
>
>    [i for i in range(20) if i < 50 or stop()]  # appears to work fine
>    [i for i in range(20) if i < 10 or stop()]  # breaks

As I said I would prefer a solution that would work for list
comprehensions but there isn't one so the stop() method has to come
with the caveat that it can only be used in that way. That said, I
have become used to using a generator inside a call to dict() or set()
(since the comprehensions for those cases were only recently added) so
it doesn't seem a big problem to rewrite the above with calls to
list().

You are right, though, that a bug like this would be problematic. If
the StopIteration leaks up the call stack into a generator that is
being for-looped then it creates a confusing debug problem (at least
it did the first time I encountered it).

>
> 3) It reads wrong for a Python boolean expression. Given an if clause:
>
>        if cond1() or cond2()
>
>     you should expect that an element is generated if either cond1 or cond2
>     are true. When I see "if cond1() or stop()" I don't read it as "stop if
>     not cond1()" but as a Python bool expression, "generate an element if
>     cond1() gives a truthy value or if stop() gives a truthy value".

Again I would have preferred 'else break' or something clearer but
this seems the best available (I'm open to suggestions).

>
> This "if cond or stop()" is a neat hack, but it's still a hack, and less
> readable and understandable than I expect from Python code.

It is a hack (and I would prefer a supported method) but my point was
that both you and Yuriy wrote the wrong code without noticing it. You
both posted it to a mailing list where no one else noticed until
someone actually tried running the code. In other words it wasn't
obvious that the code was incorrect just from looking at it.

This one looks strange but if you knew what stop() was then you would
understand it:
    list(x for x in range(100) if x < 50 or stop())

This one is difficult to mentally parse even if you understand all of
the constituent parts:
    [x for x in takewhile(partial(lt, 50), range(100))]


Oscar



More information about the Python-ideas mailing list