Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

reversed(zip(...)) not working as intended

3,690 views
Skip to first unread message

Sven R. Kunze

unread,
Mar 6, 2016, 1:30:22 PM3/6/16
to
Hi,

what's the reason that reversed(zip(...)) raises as a TypeError?

Would allowing reversed to handle zip and related functions lead to
strange errors?

Best,
Sven

Tim Chase

unread,
Mar 6, 2016, 1:42:13 PM3/6/16
to
Peculiar, as this works in 2.x but falls over in 3.x:

$ python
Python 2.7.9 (default, Mar 1 2015, 12:57:24)
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> list(reversed(zip(range(10), range(20,100))))
[(9, 29), (8, 28), (7, 27), (6, 26), (5, 25), (4, 24), (3, 23), (2,
22), (1, 21), (0, 20)]

$ python3
Python 3.4.2 (default, Oct 8 2014, 10:45:20)
[GCC 4.9.1] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> list(reversed(zip(range(10), range(20,100))))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: argument to reversed() must be a sequence


I'm not sure why reversed() doesn't think that the thing returned by
zip() isn't a sequence.

-tkc

Albert-Jan Roskam

unread,
Mar 6, 2016, 1:47:26 PM3/6/16
to
(Sorry for top-posting)

No TypeError here:

Python 2.7.2 (default, Nov 2 2015, 01:07:37) [GCC 4.9 20140827 (prerelease)] on linux4
Type "help", "copyright", "credits" or "license" for more information.
>>> ten = range(10)
>>> reversed(zip(ten, ten))
<listreverseiterator object at 0xb6c0d310>
>>> list(reversed(zip(ten, ten)))
[(9, 9), (8, 8), (7, 7), (6, 6), (5, 5), (4, 4), (3, 3), (2, 2), (1, 1), (0, 0)]
>>>

> To: pytho...@python.org
> From: srk...@mail.de
> Subject: reversed(zip(...)) not working as intended
> Date: Sun, 6 Mar 2016 19:29:59 +0100
>
> Hi,
>
> what's the reason that reversed(zip(...)) raises as a TypeError?
>
> Would allowing reversed to handle zip and related functions lead to
> strange errors?
>
> Best,
> Sven
> --
> https://mail.python.org/mailman/listinfo/python-list

Peter Otten

unread,
Mar 6, 2016, 1:54:01 PM3/6/16
to
Sven R. Kunze wrote:

> what's the reason that reversed(zip(...)) raises as a TypeError?
>
> Would allowing reversed to handle zip and related functions lead to
> strange errors?

In Python 3 zip() can deal with infinite iterables -- what would you expect
from

reversed(zip(count()))

?

If all arguments of zip() are finite and of equal length you can write

zip(reversed(a1), reversed(a2), ...)

or if you find that really useful something like

>>> class myzip(zip):
... def __init__(self, *args):
... self.args = args
... def __reversed__(self):
... return zip(*(reversed(a) for a in self.args))
...
>>> list(reversed(myzip("abc", [1,2,3])))
[('c', 3), ('b', 2), ('a', 1)]

While this might look right at first sight it really opens a can of worms.
First zip():

>>> z = zip("abc", "def")
>>> next(z)
('a', 'd')
>>> list(z)
[('b', 'e'), ('c', 'f')]

Now myzip():

>>> m = myzip("abc", "def")
>>> next(m)
('a', 'd')
>>> list(reversed(m))
[('c', 'f'), ('b', 'e'), ('a', 'd')]

Frankly, I have no idea what consistent behaviour should look like for a
zip() that can be "reverse-iterated".

PS: In Python 2 zip() would produce a list, so

>>> list(reversed(zip("abc", "def")))
[('c', 'f'), ('b', 'e'), ('a', 'd')]

worked without requiring any code in zip().

Tim Chase

unread,
Mar 6, 2016, 1:55:17 PM3/6/16
to
On 2016-03-06 12:38, Tim Chase wrote:
> On 2016-03-06 19:29, Sven R. Kunze wrote:
> > what's the reason that reversed(zip(...)) raises as a TypeError?
>
> I'm not sure why reversed() doesn't think that the thing returned by
> zip() isn't a sequence.

Ah, a little more digging suggests that in 2.x, zip() returned a list
which "has a __reversed__() method [and] supports the sequence
protocol (the __len__() method and the __getitem__() method with
integer arguments starting at 0)."

In 3.x, zip() returns a generic iterator which neither has a
__reversed__() method nor has __len__() and __getitem__() methods.

So it looks like one needs to either

results = reversed(list(zip(...)))

or, more efficiently (doing it with one less duplication of the list)

results = list(zip(...))
results.reverse()

-tkc


Peter Otten

unread,
Mar 6, 2016, 2:00:06 PM3/6/16
to
Tim Chase wrote:

> On 2016-03-06 19:29, Sven R. Kunze wrote:
>> what's the reason that reversed(zip(...)) raises as a TypeError?
>>
>> Would allowing reversed to handle zip and related functions lead to
>> strange errors?
>
> Peculiar, as this works in 2.x but falls over in 3.x:
>
> $ python
> Python 2.7.9 (default, Mar 1 2015, 12:57:24)
> [GCC 4.9.2] on linux2
> Type "help", "copyright", "credits" or "license" for more information.
>>>> list(reversed(zip(range(10), range(20,100))))
> [(9, 29), (8, 28), (7, 27), (6, 26), (5, 25), (4, 24), (3, 23), (2,
> 22), (1, 21), (0, 20)]
>
> $ python3
> Python 3.4.2 (default, Oct 8 2014, 10:45:20)
> [GCC 4.9.1] on linux
> Type "help", "copyright", "credits" or "license" for more information.
>>>> list(reversed(zip(range(10), range(20,100))))
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> TypeError: argument to reversed() must be a sequence
>
>
> I'm not sure why reversed() doesn't think that the thing returned by
> zip() isn't a sequence.

Because it isn't ;)

[Python 3]

>>> zip("abc")
<zip object at 0x7f6186d27e48>

>>> import collections
>>> isinstance(z, collections.Sequence)
False
>>> isinstance(z, collections.Iterator)
True

zip() in Python 3 is what itertools.izip() used to be in Python 2.

MRAB

unread,
Mar 6, 2016, 2:04:17 PM3/6/16
to
On 2016-03-06 18:29, Sven R. Kunze wrote:
> Hi,
>
> what's the reason that reversed(zip(...)) raises as a TypeError?
>
> Would allowing reversed to handle zip and related functions lead to
> strange errors?
>
'reversed' yields the items in reverse order; it needs the last item first.

Iterators yield items from the first to the last.

'reversed' would have to get and store all of the items from the
iterator. It won't know which is the last item until the iterator raises
StopIteration.

Only then will it be able to yield the items in reverse order.

It's much better for it to complain, and leave it for the user to do it
explicitly with reversed(list(zip(...))).

Sven R. Kunze

unread,
Mar 6, 2016, 2:52:58 PM3/6/16
to
On 06.03.2016 19:53, Peter Otten wrote:
> Sven R. Kunze wrote:
>
>> what's the reason that reversed(zip(...)) raises as a TypeError?
>>
>> Would allowing reversed to handle zip and related functions lead to
>> strange errors?
> In Python 3 zip() can deal with infinite iterables -- what would you expect
> from
>
> reversed(zip(count()))
>
> ?

Have I no idea. ;-)

But to me, "infinite" feels not like the most common use-case to most
developers.

I just stumbled over it during rapid test case development:

for c in reversed(zip(ascii_lowercase, ascii_uppercase)):
...

Bam! That doesn't work although the code clearly describes what to do. :-(

Sven R. Kunze

unread,
Mar 6, 2016, 2:55:51 PM3/6/16
to
On 06.03.2016 19:51, Tim Chase wrote:
> So it looks like one needs to either
>
> results = reversed(list(zip(...)))
>
> or, more efficiently (doing it with one less duplication of the list)
>
> results = list(zip(...))
> results.reverse()

Nice idea. :) Unfortunately, I used it while drafting some unittests and
I love SHORT oneliners:

for c in reversed(zip(ascii_lowercase, ascii_uppercase)):
...

ooops. :-/

Best,
Sven
0 new messages