On 9/11/2011 12:01 AM, Ian Kelly wrote:
On Sat, Sep 10, 2011 at 1:36 PM, Terry Reedy<tjre...@udel.edu>  wrote:
The statement containing the explicit next(items) call can optionally be
wrapped to explicitly handle the case of an empty iterable in whatever
manner is desired.

try:
    <set up with next(items)>
except StopIteration:
    raise ValueError("iterable cannot be empty")

The only time that's optional

This is an opinion, or rather, a programming philosophy based on technical facts, rather than a fact itself. You do raise an important issue.

is when you're writing an iterator and
the try-except would end up looking like this:

try:
     # do stuff with next(items)
except StopIteration:
     raise StopIteration

And even then, it's probably a good idea to clearly document that
you're allowing a StopIteration from one iterator to propagate up as a
StopIteration for another.

In the yield-pairs example,

def pairs(iterable):
    it = iter(iterable)
    for i in it:
        yield i, next(it)

ignoring StopIteration from the get-even explicit next means ignoring an odd item. If pairs() were in a general purpose library, it should have a doc string that specifies that, and a test case with an odd number of items. I would consider a comment in the code itself to be optional, depending on the intended or expected human audience and the context of presentation. In this context, the explanation is in the text that surrounds the code.

Apart from that case, whenever you call next() you should always be
prepared to catch a StopIteration.

To me, it depends on the contract or specification of the function. If the behavior for an empty input iterator is not specified, then there is no basis for writing the body of an except clause.

While in the past few months I have written examples of all of the three explicit-next use cases I gave, I was prompted to write them up now by Tigerstyle's 'Doctest failing' thread. The specification by example (perhaps given by an instructor) did not include an empty title that would lead to an empty list of title words. Certainly, the doc for

def fix_title(title):
    small_words = ('into', 'the', 'a', 'of', 'at', 'in', 'for', 'on')
    twords = iter(title.strip().lower().split())
    new_title = [next(twords)]
    for word in twords:
        if word not in small_words:
            word = word.title()
        new_title.append(word)
    return(' '.join(new_title))

should start "Given a title with at least one word, ...".

The Agile Programming Test-Driven-Development maxim, 'Write the minimum code needed to pass the test' says that the extra lines needed to catch and process StopIteration should *not* be written until there is a test case leading to such.

Letting a StopIteration propagate
up the stack to parts unknown is bad juju because it's a flow control
exception, not an error-signaling exception.  If it happens to
propagate up to another for loop, then it will break out of the for
loop, and the exception will simply be swallowed.

What you are saying is a) that the following code

for title in ['amazinG', 'a helL of a fiGHT', '', 'igNordEd']:
    print(fix_title(title))

will print 'Amazing', 'A Hell of a Fight', and stop; b) that this is the worst choice of how to handle the empty title; and c) that in real world world programming, *someone* should decide whether fix_title('') returns '' or raises ValueError.

A counter-argument could be 1. that when a function's contract is violated, invoking unspecified behavior, anything is allowed; or 2. that titles are checked for actual content before the case fixup is called, and the time and mental energy required to define and test behavior for impossible input is wasted and better spent on something more useful.

--
Terry Jan Reedy

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

Reply via email to