Exception handling in Python 3.x

  • Thread starter Steven D'Aprano
  • Start date
S

Steve Holden

PEP 255, like too much Python literature, doesn't distinguish clearly
between the language definition and implementation detail. It says
"The mechanics of StopIteration are low-level details, much like the
mechanics of IndexError in Python 2.1". Applications shouldn't be
explicitly using StopIteration.
So you don't think that we should rely on iterables with no __iter__()
method to raise IndexError to terminate iterations when their
__getitem__() is called with an invalid index? The IndexError mechanism
was, to the best of my less-than-complete knowledge, used by all pre-2.2
implementations. The quoted paragraph appears to be intended to reassure
the applications programmer that there is no normal need to handle
StopIteration specially - just as there was no need to handle IndexError
specially.
IronPython doesn't do StopIteration the same way CPython does.

http://ironpython.codeplex.com/wikipage?title=IPy1.0.xCPyDifferences
Perhaps not, but the only difference is what happens on repeated calls
to next() after the iterator is exhausted. The iterator still terminates
by raising a StopIteration error.

I have no idea what Shed Skin does, but to the extent that iterators
don't raise StopIteration on exhaustion I'd say it is in error.

regards
Steve
 
S

Steve Holden

Perhaps Python could use Guido's time machine to check whether the
sequence will yield another object in the future. :)

Since there's only one time machine that would effectively be a lock
across all Python interpreters.

regards
Steve
 
S

Steve Holden

Perhaps Python could use Guido's time machine to check whether the
sequence will yield another object in the future. :)

Since there's only one time machine that would effectively be a lock
across all Python interpreters.

regards
Steve
 
A

Arnaud Delobelle

Paul Rubin said:
Steven D'Aprano said:
Apart from this horrible idiom:

def func(iterable):
it = iter(iterable)
failed = False
try:
x = next(it)
except StopIteration:
failed = True
if failed:
raise ValueError("can't process empty iterable")
print(x)


or similar, is there really no way to avoid these chained exceptions?

Seems like yet another example of people doing messy things with
exceptions that can easily be done with iterators and itertools:

from itertools import islice

def func(iterable):
xs = list(islice(iter(iterable), 1))
if len(xs) == 0:
raise ValueError(...)
print xs[0]

It's really unfortunate, though, that Python 3 didn't offer a way to
peek at the next element of an iterable and test emptiness directly.

I missed the start of this discussion but there are two simpler ways:

def func(iterable):
for x in iterable:
print(x)
return
raise ValueError("... empty iterable")

Or using 3.x's next's optional second argument:

_nonext=object()
def func(iterable):
x = next(iter(iterable), _nonext)
if x is _nonext:
raise ValueError("... empty iterable")
print(x)
 
E

Ethan Furman

Arnaud said:
I missed the start of this discussion but there are two simpler ways:

def func(iterable):
for x in iterable:
print(x)
return
raise ValueError("... empty iterable")


For the immediate case this is a cool solution.

Unfortunately, it doesn't fix the unwanted nesting of exceptions problem.

~Ethan~
 
E

Ethan Furman

Ethan said:
For the immediate case this is a cool solution.


Drat -- I have to take that back -- the OP stated:
> The intention is:
>
> * detect an empty iterator by catching StopIteration;
> * if the iterator is empty, raise a ValueError;
> * otherwise process the iterator.


Presumably, the print(x) would be replaced with code that processed the
entire iterable (including x, of course), and not just its first element.

~Ethan~
 
A

Arnaud Delobelle

Ethan Furman said:
Drat -- I have to take that back -- the OP stated:



Presumably, the print(x) would be replaced with code that processed
the entire iterable (including x, of course), and not just its first
element.

As I had stated before, I didn't where the discussion started from. I
replied to code posted by Steven D'Aprano and Paul Rubin. My code
snippet was equivalent in functionality to theirs, only a little
simpler.

Now if one wants to raise an exception if an iterator is empty, else
process it somehow, it must mean that the iterator needs to have at
least one element for the processing to be meaningful and so it can be
thought of as a function of one element and of one iterator:

process(first, others)

which never needs to raise an exception (at least related to the number
of items in the iterator). Therefore you can write your function as
follows:

def func(iterable):
iterator = iter(iterable)
for first in iterable:
return process(first, iterator)
else:
raise ValueError("need non-empty iterable")
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,196
Latest member
ScottChare

Latest Threads

Top