Dangerous behavior of list(generator)

exarkun at twistedmatrix.com exarkun at twistedmatrix.com
Mon Dec 14 10:21:09 EST 2009


On 02:58 pm, mal at egenix.com wrote:
>exarkun at twistedmatrix.com wrote:
>>On 08:45 am, tjreedy at udel.edu wrote:
>>>Tom Machinski wrote:
>>>>In most cases, `list(generator)` works as expected. Thus,
>>>>`list(<generator expression>)` is generally equivalent to 
>>>>`[<generator
>>>>expression>]`.
>>>>
>>>>Here's a minimal case where this equivalence breaks, causing a 
>>>>serious
>>>>and hard-to-detect bug in a program:
>>>>
>>>>   >>> def sit(): raise StopIteration()
>>>
>>>StopIteration is intended to be used only within the .__next__ method
>>>of iterators. The devs know that other 'off-label' use results in the
>>>inconsistency you noted, but their and my view is 'don't do that'.
>>
>>Which is unfortunate, because it's not that hard to get StopIteration
>>without explicitly raising it yourself and this behavior makes it
>>difficult to debug such situations.
>>
>>What's with this view, exactly?  Is it just that it's hard to 
>>implement
>>the more desirable behavior?
>
>I'm not exactly sure what you're asking for.
>
>The StopIteration exception originated as part of the for-loop
>protocol. Later on it was generalized to apply to generators
>as well.
>
>The reason for using an exception is simple: raising and catching
>exceptions is fast at C level and since the machinery for
>communicating exceptions up the call stack was already there
>(and doesn't interfere with the regular return values), this
>was a convenient method to let the upper call levels know
>that an iteration has ended (e.g. a for-loop 4 levels up the
>stack).
>
>I'm not sure whether that answers your question, but it's the
>reason for things being as they are :-)

I'm asking about why the behavior of a StopIteration exception being 
handled from the `expression` of a generator expression to mean "stop 
the loop" is accepted by "the devs" as acceptable.  To continue your 
comparison to for loops, it's as if a loop like this:

    for a in b:
        c

actually meant this:

    for a in b:
        try:
            c
        except StopIteration:
            break

Note, I know *why* the implementation leads to this behavior.  I'm 
asking why "the devs" *accept* this.

Jean-Paul



More information about the Python-list mailing list