On Wed, Dec 11, 2019 at 7:46 PM Steven D'Aprano <st...@pearwood.info> wrote:
> There's a difference between the first day of *the rest* of your life
> and the first day of your life.
>
> There's a difference between the first item of *the rest* of the
> iterator and the first item of the iterator.
>
> Cliches and platitudes will only take you so far. What counts here is
> the behaviour of the code. When you iterate over an iterable using a for
> loop, you get the same sequence of items whether it is an iterator or
> not. But that's not what happens if you call `first(iterable)` multiple
> times. Calling it once is fine, but people will call it multiple times.

An iterator doesn't HAVE anything other than "the rest". By
definition, an iterator's contents is the same as the iterator's
remaining contents. Do you consider it a fundamental flaw of the "in"
operator that, when used with an iterator, it is destructive?

>>> x = [1, 2, 3, 4, 5]
>>> 3 in x
True
>>> 3 in x
True
>>> x = iter(x)
>>> 3 in x
True
>>> 3 in x
False

Does the meaning of "in" change when used on an iterator? Or is this
an acceptable consequence of the inherently destructive nature of
querying an iterator?

> I am writing some code as we speak to process a bunch of lines of text
> from an iterable. I'm expecting a mandatory header as the first line, an
> optional line of dashes "-------", and then one or more lines that need
> processing. If all I remembered is that `first` works on both iterators
> and non-iterators, I might write something like this:
>
>
>     def process(lines):
>         # can't use next
>         header = first(lines, '')
>         line = first(lines, '')
>         if is_dashes(line):
>             line = first(lines, '')
>         while line:
>             do_stuff(line)
>             line = first(lines, '')
>
>
> Seems reasonable, if you read "first" as meaning "first line in the
> remaining iterator". But as soon as I pass a concrete sequence of lines,
> rather than an iterator, I'll have an infinite loop.
>
> Let me try to anticipate your likely objection:
>
>     "Well of course it's not going to work, you're calling `first`
>     instead of `next`. When you want to consume items, you should
>     call `next`."
>
>
> Okay, but isn't it your position that the difference between "first" and
> "next" is a difference that makes no difference? (See quote below.)
>
> Obviously I could re-write that function in many ways, but the simplest
> fix is to call `lines = iter(lines)` at the top of the function.
>
> But if I do that, I don't need "first", I can just use `next`, which
> reads better. What does "first" give me?

Nothing. It's not the tool for this job. You're trying to shoehorn
first() into a job that isn't its job.

> I know, I can use it to look-ahead into a sequence:
>
>     head = first(lines)  # like lines[0] but works if it's empty
>     if head is None:
>         print("empty")
>     else:
>         do_stuff(lines)
>
>
> Works fine... until I pass an iterator, and then wonder why the first
> line is skipped.
>
> The thing is, we're fooled by the close similarity of iteration over
> iterators and other iterables (sequences and containers). Destructive
> iteration and non-destructive iteration is a big difference. Utility
> functions like the proposed `first` that try to pretend there is no such
> difference are, I believe, a gotcha waiting to happen.

And ordered vs unordered is also a big difference. Should first()
raise an error with sets because there's no real concept of "the first
element"? With a list, first(x) will remain the same value even if you
add more to the end of the list, but unrelated mutations to a set
might change which element is "first". Does that mean that first() and
next() are undefined for sets? No. We just accept that there are these
differences.

ChrisA
_______________________________________________
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/5R5JPNP3UFSUIM6RGP6RT5H5CTGNO2BB/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to