Why does Python have the built-in function reversed?
Why not just use x[::-1] instead of reversed(x)?
Edit: #TanveerAlam pointed out that reversed is not actually a function, but rather a class, despite being listed on the page Built-in Functions.
>>> a= [1,2,3,4,5,6,7,8,9,10]
>>> a[::-1]
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> reversed(a)
<listreverseiterator object at 0x10dbf5390>
The first notation is generating the reverse eagerly; the second is giving you a reverse iterator, which is possibly cheaper to acquire, as it has potential to only generate elements as needed
reversed returns a reverse iterator.
[::-1] asks the object for a slice
Python objects try to return what you probably expect
>>> [1, 2, 3][::-1]
[3, 2, 1]
>>> "123"[::-1]
'321'
This is convenient - particularly for strings and tuples.
But remember the majority of code doesn't need to reverse strings.
The most important role of reversed() is making code easier to read and understand.
The fact that it returns an iterator without creating a new sequence is of secondary importance
From the docs
PEP 322: Reverse Iteration A new built-in function, reversed(seq)(),
takes a sequence and returns an iterator that loops over the elements
of the sequence in reverse order.
>>>
>>> for i in reversed(xrange(1,4)):
... print i
...
3
2
1
Compared to extended slicing, such as range(1,4)[::-1], reversed() is
easier to read, runs faster, and uses substantially less memory.
Note that reversed() only accepts sequences, not arbitrary iterators.
If you want to reverse an iterator, first convert it to a list with
list().
>>>
>>> input = open('/etc/passwd', 'r')
>>> for line in reversed(list(input)):
... print line
...
reversed return a reverse iterator.
x[::-1] return a list.
In [1]: aaa = [1,2,3,4,5]
In [4]: aaa[::-1]
Out[4]: [5, 4, 3, 2, 1]
In [5]: timeit(aaa[::-1])
1000000 loops, best of 3: 206 ns per loop
In [6]: reversed(aaa)
Out[6]: <listreverseiterator at 0x104310d50>
In [7]: timeit(reversed(aaa))
10000000 loops, best of 3: 182 ns per loop
First of all, reversed is not a built-in function.
>>> type(reversed)
<type 'type'>
It's a class which itrates over a sequence and gives a reverse order of sequence.
Try:
>>> help(reversed)
Help on class reversed in module __builtin__:
class reversed(object)
| reversed(sequence) -> reverse iterator over values of the sequence
And when we pass a parameter to it, it acts as a iterator,
>>> l = [1, 2, 3, 4]
>>> obj = reversed(l)
>>> obj
<listreverseiterator object at 0x0220F950>
>>> obj.next()
4
>>> obj.next()
3
>>> obj.next()
2
>>> obj.next()
1
>>> obj.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
whereas a slice operation returns the whole list which is not memory efficient for larger lists.
That is why, in Python 2, we have range (which returns whole list) as well as xrange (which generates each element on every iteration).
>>> l[::-1]
[4, 3, 2, 1]
aList = [123, 'xyz', 'zara', 'abc', 'xyz'];
print type(reversed(aList))
bList = [123, 'xyz', 'zara', 'abc', 'xyz'];
print type(bList[::-1])
Output:
<type 'listreverseiterator'>
<type 'list'>
The reversed function returns a reverse iterator. The [::-1] returns a list.
Because reversed returns an iterator.
Related
This question already has answers here:
Why do these list operations (methods: clear / extend / reverse / append / sort / remove) return None, rather than the resulting list?
(6 answers)
Alternatives to using in-place list methods within a list comprehension?
(3 answers)
Closed 6 months ago.
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 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.
What is the difference between add and update operations in python if i just want to add a single value to the set.
a = set()
a.update([1]) #works
a.add(1) #works
a.update([1,2])#works
a.add([1,2])#fails
Can someone explain why is this so.
set.add
set.add adds an individual element to the set. So,
>>> a = set()
>>> a.add(1)
>>> a
set([1])
works, but it cannot work with an iterable, unless it is hashable. That is the reason why a.add([1, 2]) fails.
>>> a.add([1, 2])
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: unhashable type: 'list'
Here, [1, 2] is treated as the element being added to the set and as the error message says, a list cannot be hashed but all the elements of a set are expected to be hashables. Quoting the documentation,
Return a new set or frozenset object whose elements are taken from iterable. The elements of a set must be hashable.
set.update
In case of set.update, you can pass multiple iterables to it and it will iterate all iterables and will include the individual elements in the set. Remember: It can accept only iterables. That is why you are getting an error when you try to update it with 1
>>> a.update(1)
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: 'int' object is not iterable
But, the following would work because the list [1] is iterated and the elements of the list are added to the set.
>>> a.update([1])
>>> a
set([1])
set.update is basically an equivalent of in-place set union operation. Consider the following cases
>>> set([1, 2]) | set([3, 4]) | set([1, 3])
set([1, 2, 3, 4])
>>> set([1, 2]) | set(range(3, 5)) | set(i for i in range(1, 5) if i % 2 == 1)
set([1, 2, 3, 4])
Here, we explicitly convert all the iterables to sets and then we find the union. There are multiple intermediate sets and unions. In this case, set.update serves as a good helper function. Since it accepts any iterable, you can simply do
>>> a.update([1, 2], range(3, 5), (i for i in range(1, 5) if i % 2 == 1))
>>> a
set([1, 2, 3, 4])
add is faster for a single element because it is exactly for that purpose, adding a single element:
In [5]: timeit a.update([1])
10000000 loops, best of 3: 191 ns per loop
In [6]: timeit a.add(1)
10000000 loops, best of 3: 69.9 ns per loop
update expects an iterable or iterables so if you have a single hashable element to add then use add if you have an iterable or iterables of hashable elements to add use update.
s.add(x) add element x to set s
s.update(t) s |= t return set s with elements added from t
add adds an element, update "adds" another iterable set, list or tuple, for example:
In [2]: my_set = {1,2,3}
In [3]: my_set.add(5)
In [4]: my_set
Out[4]: set([1, 2, 3, 5])
In [5]: my_set.update({6,7})
In [6]: my_set
Out[6]: set([1, 2, 3, 5, 6, 7])
.add() is intended for a single element, while .update() is for the introduction of other sets.
From help():
add(...)
Add an element to a set.
This has no effect if the element is already present.
update(...)
Update a set with the union of itself and others.
add only accepts a hashable type. A list is not hashable.
a.update(1) in your code won't work. add accepts an element and put it in the set if it is not already there but update takes an iterable and makes a unions of the set with that iterable. It's kind of like append and extend for the lists.
I guess no one mentioned about the good resource from Hackerrank. I'd like to paste how Hackerrank mentions the difference between update and add for set in python.
Sets are unordered bag of unique values. A single set contains values of any immutable data type.
CREATING SET
myset = {1, 2} # Directly assigning values to a set
myset = set() # Initializing a set
myset = set(['a', 'b']) # Creating a set from a list
print(myset) ===> {'a', 'b'}
MODIFYING SET - add() and update()
myset.add('c')
myset ===>{'a', 'c', 'b'}
myset.add('a') # As 'a' already exists in the set, nothing happens
myset.add((5, 4))
print(myset) ===> {'a', 'c', 'b', (5, 4)}
myset.update([1, 2, 3, 4]) # update() only works for iterable objects
print(myset) ===> {'a', 1, 'c', 'b', 4, 2, (5, 4), 3}
myset.update({1, 7, 8})
print(myset) ===>{'a', 1, 'c', 'b', 4, 7, 8, 2, (5, 4), 3}
myset.update({1, 6}, [5, 13])
print(myset) ===> {'a', 1, 'c', 'b', 4, 5, 6, 7, 8, 2, (5, 4), 13, 3}
Hope it helps. For more details on Hackerrank, here is the link.
add method directly adds elements to the set while the update method converts first argument into set then it adds
the list is hashable therefore we cannot add a hashable list to unhashable set.
We use add() method to add single value to a set.
We use update() method to add sequence values to a set.
Here Sequences are any iterables including list,tuple,string,dict etc.
So I can create a reverse iterator on a list:
list(reversed([0,1,2,3]))
[3, 2, 1, 0]
I assume this simply calls getitem from index len(...)-1 to 0. But then I cannot also do this:
list(reversed(xrange(4)))
[3, 2, 1, 0]
Now I am a bit confused. Does this create the list from xrange(4) and then reverse it? If not, how does it know what the last element is and how to go backwards? I read the documentation but it didn't help.
reversed() looks for a __reversed__ special method on the object. List objects provide this, and so does xrange():
>>> xrange(4).__reversed__()
<rangeiterator object at 0x106e2fa50>
The iterator object simply produces the values in reverse, no list object is produced.
For objects that do not implement __reversed__, the reversed() function uses the length and the __getitem__ method; e.g. reversed() is essentially equivalent to:
def reversed(seq):
try:
return seq.__reversed__()
except AttributeError:
return (seq[i] for i in xrange(len(seq) - 1, -1 , -1))
where the second part is a generator expression, evaluating lazily. The generator then accesses each item in turn starting at index (length - 1) all the way down to index 0.
reversed() can only take a sequence -- if it took generic iterators, it couldn't know what the final value was without exhausting the iterator and storing all the values.
Luckily, xrange returns an xrange object that works as a sequence:
>>> x = xrange(10)
>>> len(x)
10
>>> x[9]
9
It also happens to have an actual __reversed__ method, but that's a special case of having all the sequence methods.
Just compare the two:
In [2]: reversed(xrange(4))
Out[2]: <rangeiterator at 0x7fa83291bde0>
In [3]: list(reversed(xrange(4)))
Out[3]: [3, 2, 1, 0]
In [4]: reversed([0,1,2,3])
Out[4]: <listreverseiterator at 0x7fa8328be2d0>
In [5]: list(reversed([0,1,2,3]))
Out[5]: [3, 2, 1, 0]
This question already has answers here:
What are iterator, iterable, and iteration?
(16 answers)
Closed last month.
I am studying Alex Marteli's Python in a Nutshell and the book suggests that any object that has a next() method is (or at least can be used as) an iterator. It also suggests that most iterators are built by implicit or explicit calls to a method called iter.
After reading this in the book, I felt the urge to try it. I fired up a python 2.7.3 interpreter and did this:
>>> x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> for number in range(0, 10):
... print x.next()
However the result was this:
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
AttributeError: 'list' object has no attribute 'next'
In confusion, I tried to study the structure of the x object via dir(x) and I noticed that it had a __iter__ function object. So I figured out that it can be used as an iterator, so long as it supports that type of interface.
So when I tried again, this time slightly differently, attempting to do this:
>>> _temp_iter = next(x)
I got this error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: list object is not an iterator
But how can a list NOT be an iterator, since it appears to support this interface, and can be certainly used as one in the following context:
>>> for number in x:
... print x
Could someone help me clarify this in my mind?
They are iterable, but they are not iterators. They can be passed to iter() to get an iterator for them either implicitly (e.g. via for) or explicitly, but they are not iterators in and of themselves.
You need to convert list to an iterator first using iter():
In [7]: x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [8]: it=iter(x)
In [9]: for i in range(10):
it.next()
....:
....:
Out[10]: 0
Out[10]: 1
Out[10]: 2
Out[10]: 3
Out[10]: 4
Out[10]: 5
Out[10]: 6
Out[10]: 7
Out[10]: 8
Out[10]: 9
In [12]: 'next' in dir(it)
Out[12]: True
In [13]: 'next' in dir(x)
Out[13]: False
checking whether an object is iterator or not:
In [17]: isinstance(x,collections.Iterator)
Out[17]: False
In [18]: isinstance(x,collections.Iterable)
Out[18]: True
In [19]: isinstance(it,collections.Iterable)
Out[19]: True
In [20]: isinstance(it,collections.Iterator)
Out[20]: True
Just in case you are confused about what the difference between iterables and iterators is. An iterator is an object representing a stream of data. It implements the iterator protocol:
__iter__ method
next method
Repeated calls to
the iterator’s next() method return successive items in the stream. When
no more data is available the iterator object is exhausted and any further calls to its
next() method just raise StopIteration again.
On the other side iterable objects implement the __iter__ method that when called returns an iterator, which allows for multiple passes over their data. Iterable objects are reusable, once exhausted they can be iterated over again. They can be converted to iterators using the iter function.
So if you have a list (iterable) you can do:
>>> l = [1,2,3,4]
>>> for i in l:
... print i,
1 2 3 4
>>> for i in l:
... print i,
1 2 3 4
If you convert your list into an iterator:
>>> il = l.__iter__() # equivalent to iter(l)
>>> for i in il:
... print i,
1 2 3 4
>>> for i in il:
... print i,
>>>
List is not iterator but list contains an iterator object __iter__ so when you try to use for loop on any list, for loop calls __iter__ method and gets the iterator object and then it uses next() method of list.
x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
it = x.__iter__()
Now it contains iterator object of x which you can use as it.next() until StopIteration exception is thrown
There are already good answers to it about list being an iterable but not iterator.
In python version > 3.0, use the following
a = [1, 2, 3]
b = iter(a)
b.__next__()
At the interpreter,
a = [1,2,3,4]
a = a.reverse()
Next when I type a at the interpreter, I get nothing. So it seems a = a.reverse() generates an empty list. Is this by design?
I am using python 2.5 on windows xp.
list.reverse() modifies the list in-place, returns None. But if you want to protect old list, you can use reversed() function for that, it returns an iterator.
In [1]: a=[1,2,3,4]
In [2]: print(a.reverse())
None
In [3]: a
Out[3]: [4, 3, 2, 1]
In [4]: a=[1,2,3,4]
In [5]: print(reversed(a))
<listreverseiterator object at 0x24e7e50>
In [6]: list(reversed(a))
Out[6]: [4, 3, 2, 1]
In [7]: a
Out[7]: [1, 2, 3, 4]
reverse changes list in-place, and doesn't return anything. Thus, this is the expected usage:
a = [1, 2, 3, 4]
a.reverse()
a # => [4, 3, 2, 1]
If you assign the result of reverse back to a, you will overwrite all its hard work with the nonsensical return value (None), which is where your bug comes from.
list is a mutable type, so list operations are in-place, and return None.
The built-in method reverse of a list on python doesn't return the reversed list.
It reverses the list in place.
So, if you want to reverse your list, like in your code, just do:
a = [1,2,3,4]
a.reverse()
list.reverse() just doesn't return anything, because it changes the list in-place. See this example:
>>> a = [1,2,3,4]
>>> a.reverse()
>>> a
[4, 3, 2, 1]
There also is the reversed function (actually a type, but doesn't matter here), which does not change the list in-place, but instead returns an iterator with the list items in the reverse order. Try:
>>> a = [1,2,3,4]
>>> a = list(reversed(a))
>>> a
[4, 3, 2, 1]
I think what you want to do is:
a = [1,2,3,4]
a.reverse()
a is an object and the operations work on it's data, so you don't need to assign again it.
The reverse method does the reverse 'in place' (like sort) and returns None, so after calling a.reverse() a already contains the result.