On 9/12/2011 12:55 PM, Ian Kelly wrote:
On Sun, Sep 11, 2011 at 6:45 PM, Terry Reedy<tjre...@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']):
     print(title)

Output:
amazing
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
StopIteration.

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 random response.

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

--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to