On Sat, 9 Jan 2021 at 13:29, Oscar Benjamin <oscar.j.benja...@gmail.com> wrote:
> The argument to reversed either needs to be a sequence with __len__
> and __getitem__ or an object with a __reversed__ method that returns
> an iterator. The arguments to chain have to be iterables. Every
> sequence is an iterable so there is a significant intersection between
> the possible inputs to chain and reversed. Also some non-sequences
> such as dict can work with reversed.
>
> You say it's hard to see how it could be made to work but you've shown
> precisely how it can already be done above:
>
>      reversed(chain(*args))   ==   chain(*map(reversed, reversed(args)))
>
> We can try that out and it certainly seems to work:
>
>     >>> from itertools import chain
>     >>> args = [[1, 2], [3, 4]]
>     >>> list(chain(*args))
>     [1, 2, 3, 4]
>     >>> list(chain(*map(reversed, reversed(args))))
>     [4, 3, 2, 1]
>
> This wouldn't work with chain.from_iterable without preconsuming the
> top-level iterable but in the case of chain the iterables are already
> in a *args tuple so flipping that order is always possible in a lazy
> way. That means the operation works fine if each arg in args is
> reversible. Otherwise if any arg is not reversible then it should give
> a TypeError just like reversed(set()) does except the error would
> potentially be delayed if some of the args are reversible and some are
> not.
>
> I haven't ever wanted to reverse a chain but I have wanted to be able
> to reverse an enumerate many times:
>
>     >>> reversed(enumerate([1, 2, 3]))
>     ...
>     TypeError
>
> The alternative zip(range(len(obj)-1, -1, -1), reversed(obj)) is
> fairly cryptic in comparison as well as probably being less efficient.
> There could be a __reversed__ method for enumerate with the same
> caveat as for chain: if the underlying object is not reversible then
> you get a TypeError. Otherwise reversed(enumerate(seq)) works fine for
> any sequence seq.
>
> The thornier issue is how to handle reversed if the chain/enumerate
> iterator has already been partially consumed. If it's possible just to
> give an error in that case then reversed could still be useful in the
> common case.

I think you're about right here - both chain and enumerate could
reasonably be expected to be reversible. There are some fiddly edge
cases, and some potentially weird situations (as soon as we assume
no-one would ever expect to reverse a partially consumed iterator, I
bet someone will...) which probably warrant no more than "don't do
that then" but will end up being the subject of questions/confusion.

The question is whether the change is worth the cost. For me:

1. enumerate is probably more important than chain. I use enumerate a
*lot* and I've very rarely used chain.
2. Consistency is a benefit - as we've already seen, people assume
things work by analogy with other cases, and waste time when they
don't.
3. How easy it is to write your own matters. If chain or enumerate
objects exposed the iterables they were based on, you could write your
own reverser more easily.
4. How problematic are the workarounds? reversed(list(some_iter))
works fine - is turning the iterator into a concrete list that much of
an issue?

And of course, the key point - how often do people want to do this anyway?

If someone wants to do the work to implement this, I would say go for
it - raise a bpo issue and create a PR, and see what the response is.
Getting "community support" via this list is probably not crucial for
something like this. It's more of a quality of life change than a big
feature.

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

Reply via email to