[Python-ideas] Function to return first(or last) true value from list
Steven D'Aprano
steve at pearwood.info
Fri Feb 21 00:51:17 CET 2014
On Fri, Feb 21, 2014 at 12:38:01AM +0200, אלעזר wrote:
> 2014-02-21 0:00 GMT+02:00 Steven D'Aprano <steve at pearwood.info>:
> >
> > On Thu, Feb 20, 2014 at 04:14:17PM +0000, Oscar Benjamin wrote:
> > > On 20 February 2014 16:05, Terry Reedy <tjreedy at udel.edu> wrote:
> > > >>
> > > >> An implementation of first() should raise some other exception than
> > > >> StopIteration.
> > [...]
> >
> > > It's easy enough to do if you know that bare next is a bad thing.
> >
> > Say what? Why do you say that next(it) is a bad thing?
> >
> > > More-itertools does it the way I would but has a long comment
> > > wondering whether it should actually raise StopIteration:
> > >
> https://github.com/erikrose/more-itertools/blob/master/more_itertools/more.py#L37
> > >
> > > The thing is just that bare next is not something that's widely
> > > recognised as being dangerous. I've seen examples of this kind of bug
> > > in samples from many Python aficionados (including at least one from
> > > you Terry).
> >
> > Why is it dangerous, and what kind of bug?
> >
> > If you're talking about the fact that next(it) can raise StopIteration,
> > I think you are exaggerating the danger. Firstly, quite often you don't
> > mind if it raises StopIteration, since that's what you would have done
> > anyway. Secondly, I don't see why raising StopIteration is so much more
> > dangerous than (say) IndexError or KeyError.
> >
> I had this bug just the other day. I did not plan for the empty case, since
> it was obvious that the empty case is a bug, so I relied on the exception
> being raised in this case. But I did not get the exception since it was
> caught in a completely unrelated for loop. It took me a while to figure out
> what's going on, and it would've taken even more for someone else, not
> familiar with my assumption or with the whole StopIteration thing (which I
> believe is the common case). An IndexError or a KeyError would have been
> great in such a case.
IndexError can also be caught by for-loops, under some circumstances.
(See the legacy sequence protocol for iteration.)
Without knowing what your code does, and how it is written, it's hard to
discuss in anything but generalities. But in general, any code that is
expected to raise an exception should have a test that it actually does
raise that exception. If you're raising an exception that has particular
meaning to Python, then you have to be prepared that Python might
intercept it before you can see it. Would you be surprised by this?
py> class X:
... def method(self):
... # Perform some calculation, which fails.
... raise AttributeError
... def __getattr__(self, name):
... if name == 'spam': return self.method()
...
py> x = X()
py> getattr(x, 'spam', 23)
23
If the only place you are using x.spam is inside a getattr call, then
you will never see the AttributeError. Likewise, if the only place you
are using next() is inside a for-loop iterable, then the StopIteration
exception will always be captured. (This may even be deliberate on the
part of the programmer.)
I don't see this as particularly noteworthy. It's one of the things that
people probably won't think of until they've actually seen it happen,
but programming is full of things like that. Not every tricky bug to
find is a sign that the function is "dangerous".
--
Steven
More information about the Python-ideas
mailing list