List comprehension and in place methods
Question:
I am just trying to understand what happens during list comprehension. Some methods which work on lists ‘in-place’ don’t seem to work when applied in a list comprehension:
a = [[1, 2, 3], [4, 5, 6]]
i1 = id(a[0])
for i in a: i.reverse()
>>> [[3, 2, 1], [6, 5, 4] # Works
print i1 == id(a[0]) # True, same memory address
a = [i.reverse() for i in a]
>>> [None, None] # Doesn't work
print i1 == id(a[0]) # False, new memory address
a = [i[::-1] for i in a]
>>> [[3, 2, 1], [6, 5, 4]] # Works!
print i1 == id(a[0]) # False
I am guessing this has something to do with all the elements getting copied to a different memory space. Why does i[::-1]
work whereas i.reverse()
doesn’t?
Answers:
i.reverse()
reverses the array in place and doesn’t return anything, meaning it returns None
type. That way you obtain [None, None]
from list comprehension and previous arrays’ elements are reversed at the same time.
These two shouldn’t be mixed, either use a for
and x.reverse()
, or use reversed(x)
or x[::-1]
in a list comprehension.
i.reverse()
reverses the list in-place and returns None
.
What the docs say:
list.reverse()
Reverse the elements of the list, in place
vs.
reversed(seq)
Return a reverse iterator. seq must be an object which has a
__reversed__()
method or supports the sequence protocol
(the __len__()
method and the __getitem__()
method with integer arguments starting at 0
).
Examples:
>>> xs = [1, 2, 3]
>>> id(xs)
140625121860208
>>> ys = xs[::-1]
>>> id(ys)
140625121924088
Slicing creates a new list.
>>> xs.reverse()
>>> xs
[3, 2, 1]
>>> id(xs)
140625121860208
In-place sorting/reversing retains the original list.
>>> zs = list(reversed(xs))
>>> zs
[1, 2, 3]
>>> id(zs)
140625121976400
reversed()
returns an iterator; which when turns into a list creates a new list! If you have a read of PEP 0322 — Reverse Iteration you’ll note that reversed()
does not create a new data structure but simply iteratoes over the sequence in reverse order.
This does what you intend:
>>> a = [[1, 2, 3], [4, 5, 6]]
>>> [list(reversed(i)) for i in a]
[[3, 2, 1], [6, 5, 4]]
List comprehension always returns a new list, so using the in-place reverse
method just returns the return value of reverse
, which is None
.
The function reversed()
gives you an new iterator. Converting it to
a list is for your example the same as:
>>> [i[::-1] for i in a]
Even though they look very similar, it is important to distinguish both,
the function reversed()
and the method obj.reverse()
list.reverse
reverses a list in place, and return None
.
while slice a[::-1]
creates another list and return as value.
list comprehension will take the return value
of each expression.
I am just trying to understand what happens during list comprehension. Some methods which work on lists ‘in-place’ don’t seem to work when applied in a list comprehension:
a = [[1, 2, 3], [4, 5, 6]]
i1 = id(a[0])
for i in a: i.reverse()
>>> [[3, 2, 1], [6, 5, 4] # Works
print i1 == id(a[0]) # True, same memory address
a = [i.reverse() for i in a]
>>> [None, None] # Doesn't work
print i1 == id(a[0]) # False, new memory address
a = [i[::-1] for i in a]
>>> [[3, 2, 1], [6, 5, 4]] # Works!
print i1 == id(a[0]) # False
I am guessing this has something to do with all the elements getting copied to a different memory space. Why does i[::-1]
work whereas i.reverse()
doesn’t?
i.reverse()
reverses the array in place and doesn’t return anything, meaning it returns None
type. That way you obtain [None, None]
from list comprehension and previous arrays’ elements are reversed at the same time.
These two shouldn’t be mixed, either use a for
and x.reverse()
, or use reversed(x)
or x[::-1]
in a list comprehension.
i.reverse()
reverses the list in-place and returns None
.
What the docs say:
list.reverse()
Reverse the elements of the list, in place
vs.
reversed(seq)
Return a reverse iterator. seq must be an object which has a
__reversed__()
method or supports the sequence protocol
(the__len__()
method and the__getitem__()
method with integer arguments starting at0
).
Examples:
>>> xs = [1, 2, 3]
>>> id(xs)
140625121860208
>>> ys = xs[::-1]
>>> id(ys)
140625121924088
Slicing creates a new list.
>>> xs.reverse()
>>> xs
[3, 2, 1]
>>> id(xs)
140625121860208
In-place sorting/reversing retains the original list.
>>> zs = list(reversed(xs))
>>> zs
[1, 2, 3]
>>> id(zs)
140625121976400
reversed()
returns an iterator; which when turns into a list creates a new list! If you have a read of PEP 0322 — Reverse Iteration you’ll note that reversed()
does not create a new data structure but simply iteratoes over the sequence in reverse order.
This does what you intend:
>>> a = [[1, 2, 3], [4, 5, 6]]
>>> [list(reversed(i)) for i in a]
[[3, 2, 1], [6, 5, 4]]
List comprehension always returns a new list, so using the in-place reverse
method just returns the return value of reverse
, which is None
.
The function reversed()
gives you an new iterator. Converting it to
a list is for your example the same as:
>>> [i[::-1] for i in a]
Even though they look very similar, it is important to distinguish both,
the function reversed()
and the method obj.reverse()
list.reverse
reverses a list in place, and return None
.
while slice a[::-1]
creates another list and return as value.
list comprehension will take the return value
of each expression.