On Sun, 10 Oct 2021 at 04:56, Steven D'Aprano <st...@pearwood.info> wrote:

> On Fri, Oct 08, 2021 at 01:42:35PM -0700, Christopher Barker wrote:
>
> > Anyway, I do see the benefit of adding first() to itertools -- there
> really
> > is a key problem with:
> >
> > next(iter(an_iterable))
> >
> > for newbies -- you can get really really far in Python without ever
> having
> > to call  either next() or iter(). Sure, if it's a recipe, people can use
> it
> > without really understanding it, but  having an easy and obvious solution
> > would be nice.
>
> Yes? How does that make it a *problem*? I disagree strongly that needing
> to learn a simple, basic technique is a problem to be solved.


The real problem is the fact that it raises the wrong kind of exception in
the degenerate case of an empty iterable:

In [50]: next(iter([]))
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-50-bfed92c5b1cf> in <module>
----> 1 next(iter([]))

A leaky StopIteration can wreak all sorts of havoc. There was a PEP that
attempted to solve this by turning StopIteration into RuntimeError if it
gets caught in a generator but that PEP (which was rushed through very
quickly IIRC) missed the fact that generators are not the only iterators.
It remains a problem that leaking a StopIteration into map, filter etc will
terminate iteration of an outer loop.

The culprit for the problem of leaking StopIteration is next itself which
in the 1-arg form is only really suitable for use when implementing an
iterator and not for the much more common case of simply wanting to extract
something from an iterable. Numerous threads here and on stackoverflow and
elsewhere suggesting that you can simply use next(iter(obj)) are
encouraging bug magnet code. Worse, the bug when it arises will easily
manifest in something like silent data loss and can be hard to debug.

The correct usage of next/iter in most cases would be something like:

try:
    val = next(iter(obj))
except StopIteration:
    raise AnotherError

or perhaps

val = next(iter(obj), None)
if val is None:
    raise AnotherError

The real advantage of providing first (or "take" or any of the other names
that have been proposed in the past) is that it should raise a different
exception like ValueError so that it would be safe to use by default.

--
Oscar
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/YX7ZMZ3SRLIOS2S3UNON466CZUXDPRI3/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to