20

Reversing a tuple and reversing a list returns objects of different type:

>>> reversed((1,2))
<reversed at 0x7fffe802f748>
>>> reversed([1,2])
<list_reverseiterator at 0x7fffebdd4400>

They have the same dir. Neither type is a subclass of the other.

Why is that? What can one do that the other can't?

jsbueno
  • 99,910
  • 10
  • 151
  • 209
wim
  • 338,267
  • 99
  • 616
  • 750
  • There seems to be no difference bar performance unless you are using an older version of python where you could call len on the listreverseiterator. http://bugs.python.org/issue3689. – Padraic Cunningham Oct 20 '16 at 23:38

2 Answers2

14

Basically, a list implements the __reversed__ method and returns an specialized object, while tuple falls back to the default implementation of reversed for any sequence:

>>> list.__reversed__
<method '__reversed__' of 'list' objects>
>>> tuple.__reversed__
AttributeError: type object 'tuple' has no attribute '__reversed__'

Now, why list does not default to the sequence reversed object has to be found in the source code for the list object itself - probably it enables some optimizations by accessing directly some of internal list attributes.

Actually looking at the C code, there is little difference, and certainly nothing that catches the eye -

I'd dare say the special list __reversed__ implementation is a leftover from Python2 days where reversed would actually copy any other Python sequence to a list - so there would be no sense for other sequences to have special cases for it (and when they did implement the general enumreverse it was just good enough for tuples).

I am pretty sure that if one would simply comment out the __reversed__ slot on listobject.c, Python and its lists would work as if nothing had happened, defaulting to the general case reversed.

wim
  • 338,267
  • 99
  • 616
  • 750
jsbueno
  • 99,910
  • 10
  • 151
  • 209
  • 3
    Default reversed: https://github.com/python/cpython/blob/master/Objects/enumobject.c#L230 list reversed: https://github.com/python/cpython/blob/master/Objects/listobject.c#L2823 – BlackBear Oct 20 '16 at 23:09
  • 3
    What can one do that the other can't? – wim Oct 20 '16 at 23:11
  • 5
    **Github pro-tip**: If you link to a line number in blob/master like that, the links rot as soon as the file is modified. Instead, select the line(s) you want and then press the `y` key, to pin the link to a specific commit. Then the line number will never drift away from the relevant code. I've updated the links in your answer. – wim Oct 21 '16 at 04:18
  • @wim Thanks. TIL about 'y'. I knew about the link rot, but always found the commits by hand. – Mad Physicist Jul 31 '17 at 01:58
  • Would there be any reason to have a different iterator because `list` is mutable perhaps? – Mad Physicist Jul 31 '17 at 02:00
5

According to Python's documentation:

object.__reversed__(self)

Called (if present) by the reversed() built-in to implement reverse iteration. It should return a new iterator object that iterates over all the objects in the container in reverse order.

If the __reversed__() method is not provided, the reversed() built-in will fall back to using the sequence protocol (__len__() and __getitem__()). Objects that support the sequence protocol should only provide __reversed__() if they can provide an implementation that is more efficient than the one provided by reversed().

TigerhawkT3
  • 48,464
  • 6
  • 60
  • 97
Hisham Karam
  • 1,288
  • 17
  • 28
  • 3
    It seems odd that list would have a more efficient implementation available, but tuple doesn't. – wim Oct 20 '16 at 23:19