Idioms combining 'next(items)' and 'for item in items:'
tjreedy at udel.edu
Mon Sep 12 16:51:01 EDT 2011
On 9/12/2011 12:55 PM, Ian Kelly wrote:
> On Sun, Sep 11, 2011 at 6:45 PM, Terry Reedy<tjreedy at udel.edu> wrote:
>> whereas, you are right, it breaks it noisily in the body. So Ian's claim
>> that StopIteration must be caught to avoid silent termination is not true.
>> Thanks for pointing out what I saw but did not cognize the full implication
>> of before. A better exception and an error message with an explaination
>> might still be a good idea, though.
> But you can't write the function under the assumption that it will
> only be called from the function body.
Sigh. You are right.
> The following is a slight
> reorganization of your example that does exhibit the problem:
> for title in map(fix_title, ['amazinG', 'a helL of a fiGHT', '', 'igNordEd']):
> a Hell of a Fight
> Note that at first glance, my example would appear to be functionally
> equivalent to yours -- I've merely pulled the fix_title call out of
> the loop body and into the iterator. But actually they produce
> different results because fix_title misbehaves by not catching the
You are right, non-iterators should not raise or pass on StopIteration.
There are actually several reasons.
1. The manual defined StopIteration as meaning '[I have] no more values
[to give you]'. This is only meaningful coming from an iterator.
2. Your particular point is that StopIteration is (almost) unique in
being sometimes, but only sometimes, caught by the interpreter, rather
than just by user except clauses. AttributeError is another, which has
occasionally caused its own problems. But we cannot stop raising
AttributeError while we can always catch StopIteration for explicit
next() (and should outside of iterators).
3. In the case of grabbing the first item from an iterator, no first
item is a boundary case for the expected, legal type of input. I believe
boundary cases should be included in function specifications. While
there may be a couple of choices as to response, that is much less than
infinity. For fix_title, the choices are ValueError or ''. Any other
return would be an error unless explicitly given in the specs. So the
boundary case should be included in the test suite to exclude any other
4. StopIteration is an artifact of the choice of implementation. Pulling
the first item out before the loop is an alternative to a flag and
testing within the loop. Such an implementation detail should not leak
into the user's view of the function as an abstraction.
If fix_title were a generator function whose instances yielded fixed
title words one at a time, then the bare next() would be correct (as you
noted). But it is not, and the difference is important, more important
than having 'minimal clean code'. Thank you for persisting until I saw
that in this context.
Terry Jan Reedy
More information about the Python-list