Reversing order of a tuple of list - python

I have a tuple ([1,2,3], [4,5,6]) and I want to reverse the list inside so that it becomes ([3,2,1],[6,5,4]) without having to create a new tuple and adding elements to it. The closest I've gotten is:
my_func(....):
for i in tuple(...):
i = i[::-1]
return tuple(....)
problem is that while i prints out what I want..the tuple was never changed

Tuples are immutable but the objects contained by them can be either mutable or immutable, so if we modify a mutable object contained by the tuple then the change will be reflected at all the references of that object, that includes the tuple as well.
As in this case we've lists, so, all we need to do is to loop over the tuple and call .reverse() on the lists.
>>> t = ([1,2,3], [4,5,6])
>>> for x in t:
... x.reverse()
...
>>> t
([3, 2, 1], [6, 5, 4])
Another example:
>>> x = [1, 2, 3]
>>> t = (x, 'abc')
>>> x.reverse()
>>> t
([3, 2, 1], 'abc')
>>> x
[3, 2, 1]

As frostnational says, tuples are immutable, so you will have to create new ones.
You could do something like this:
>>> a = ([1,2,3],[4,5,6])
>>> b = map(lambda t: reversed(t), a)
>>> for sublist in b:
... for item in sublist:
... print(item)
...
3
2
1
6
5
4

Related

Python: Why does reversed(a)==reversed(a) returns False where a is an array?

This is the code
>>> a=[1,3,2]
>>> a
[1, 3, 2]
>>> a= 3,1,2
>>> a
(3, 1, 2)
>>> sorted(a)
[1, 2, 3]
>>> sorted(a)==sorted(a)
True
>>> reversed(a)==reversed(a)
False
Further
>>> b= reversed(a)
>>> sorted(b)==sorted(b)
False
>>> sorted(b)==sorted(b)
True
I saw this in a youtube video and can't figure out what's happening.
and that guy also showed
>>> sorted(b)
[]
sorted returns a new, sorted list. reversed returns a reverse iterator. Comparing two lists with the same elements for equality yields true. Comparing two different iterators doesn't.
If you were to compare lists constructed with the reverse iterator, you'd get True:
>>> reversed(a) == reversed(a)
False
>>> list(reversed(a)) == list(reversed(a))
True
Because reversed() returns an iterator while sorted() returns a new list.
The lists that you get back from sorted() are equal lists:
>>> a = [1, 2, 3]
>>> sorted(a)
[1, 2, 3]
>>> sorted(a)
[1, 2, 3]
>>> sorted(a) == sorted(a) # The equality here is checking that the lists contain the same objects in the same order
True
>>> sorted(a)[0] is sorted(a)[0]
True
>>> sorted(a) is sorted(a) # However, they are not the same lists, modifying one will not modify the other
False
The iterators that you get back from reversed() are going to be different each time you call it:
>>> a = [1, 2, 3]
>>> reversed(a)
<listreverseiterator object at 0x01C2A9D0>
>>> reversed(a)
<listreverseiterator object at 0x01AB4EF0>
Consuming one of these iterators twice will cause it to yield an empty list the second time as you see in your last example:
>>> a = [1, 2, 3]
>>> b = reversed(a)
>>> list(b)
[3, 2, 1]
>>> list(b)
[]
>>>
These empty lists that we get back out of the iterator will explain the 2nd exmaple:
>>> b= reversed(a)
# The first sorted(b) is the real sorted list, the second one is an empty list because the iterator has been consumed
>>> sorted(b)==sorted(b)
False
# Now both of our sorted(b) are just empty lists, since the iterator has been consumed
>>> sorted(b)==sorted(b)
True

Lists and Function Arguments

>>> def duplicate(l):
... l = l + l
...
>>> l1 = [1, 2, 3]
>>> duplicate(l1)
>>> l1
[1, 2, 3]
I believe the function above duplicates the list. But why is the result not [1, 2, 3, 1, 2, 3]?
Concatenation of two list objects (as you do with l + l) always creates a new list object. In your function you then assign that new list object back to the local variable l, which is independent of the global reference l1. The original list object is not affected because only the contents of the list were copied.
If you wanted to alter the list object in place, you need to extend l with itself:
def duplicate(l):
l.extend(l)
list.extend() copies all elements from the list you pass in and adds them to the end of the list object you called it on. Passing in the list itself is safe; it'll only copy the original elements.
Demo:
>>> def duplicate(l):
... l.extend(l)
...
>>> l1 = [1, 2, 3]
>>> duplicate(l1)
>>> l1
[1, 2, 3, 1, 2, 3]

List comprehension and in place methods [duplicate]

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.

python list appending is not working [duplicate]

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)
Closed 5 months ago.
I have something similar to:
>>> S=list()
>>> T=[1,2,3]
>>> for t in T:
... print(S.append(t))
The output I am getting is:
...
None
None
None
I expect S contains t. Why this is not working with me ?
list.append() does not return anything. Because it does not return anything, it default to None (that is why when you try print the values, you get None).
It simply appends the item to the given list in place. Observe:
>>> S = list()
>>> T = [1,2,3]
>>> for t in T:
... S.append(t)
>>> print(S)
[1, 2, 3]
Another example:
>>> A = []
>>> for i in [1, 2, 3]:
... A.append(i) # Append the value to a list
... print(A) # Printing the list after appending an item to it
...
[1]
[1, 2]
[1, 2, 3]
. append() is a list method, that does not return a value it alters the list. For example .index() or .count() methods return object values, while .append() alters the object. For example:
T = [1, 2, 3]
T.append(4)
print(T)
Result:
[1, 2, 3, 4]
We can use .append() to change the list S and add elements from the list T. Lists S, and T are two separate objects with two different addresses in the memory. With the function id() you can check that.
T = [1, 2, 3]
print(id(T))
S = list()
print(S)
print(id(S))
for t in T:
S.append(t)
print(S)
print(id(S))
Result:
2476978999688
[]
2476978081224
[1, 2, 3]
2476978081224
If you want to use only two different names (S and T) for the same list, we can write:
print(T)
print(id(T))
S = T
print(S)
print(id(S))
Result:
[1, 2, 3]
2476978999688
[1, 2, 3]
2476978999688

List being identified with another, using new items in the loop [duplicate]

This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 8 years ago.
I have a list named MyList.
I want to copy the list to a new one, then add items to the new one, so I do:
MySecondList=MyList
for item in MyList:
if item==2:
MySecondList.append(item)
The problem I am having is that the items will be added also to MyList, and as a matter of fact the loop keeps going through MyList new items too!!
Is that normal? why does it happen? shouldnt the iteration use only the original list MyList for it, instead of dinamically increase with the items I add to other list?
Yes, it is normal as lists are mutable in python and this operation:
MySecondList = MyList
simply creates a new reference to the same list object and list.append modifies the same object in-place.(other operations like +=, list.extend, list.pop etc also modify the list in-place)
You can use a shallow copy here:
MySecondList = MyList[:]
Demo:
>>> from sys import getrefcount
>>> lis = [1,2,3]
>>> foo = lis #creates a new reference to the same object [1,2,3]
>>> lis is foo
True
>>> getrefcount(lis) #number of references to the same object
3 #foo , lis and shell itself
#you can modify the list [1,2,3] from any of it's references
>>> foo.append(4)
>>> lis.append(5)
>>> foo,lis
([1, 2, 3, 4, 5], [1, 2, 3, 4, 5])
>>> lis = [1,2,3]
>>> foo = lis[:] #assigns a shallow copy of lis to foo
>>> foo is lis
False
>>> getrefcount(lis) #still 2(lis + shell_, as foo points to a different object
2
#different results here
>>> foo.append(4)
>>> lis.append(5)
>>> foo, lis
([1, 2, 3, 4], [1, 2, 3, 5])
For a lists of lists(or list of mutable objects) a shallow copy is not enough as the inner lists(or objects) are just new references to the same object:
>>> lis = [[1,2,3],[4,5,6]]
>>> foo = lis[:]
>>> foo is lis #lis and foo are different
False
>>> [id(x) for x in lis] #but inner lists are still same
[3056076428L, 3056076716L]
>>> [id(x) for x in foo] #same IDs of inner lists, i.e foo[0] is lis[0] == True
[3056076428L, 3056076716L]
>>> foo[0][0] = 100 # modifying one will affect the other as well
>>> lis[0],foo[0]
([100, 2, 3], [100, 2, 3])
For such cases use copy.deepcopy:
>>> from copy import deepcopy
>>> lis = [[1,2,3],[4,5,6]]
>>> foo = deepcopy(lis)
Because they both reference the same list (and their ids are the same). Observe:
>>> a = [1,2,3]
>>> b = a
>>> b
[1, 2, 3]
>>> a is b
True
>>> b += [1]
>>> b
[1, 2, 3, 1]
>>> a
[1, 2, 3, 1]
>>> a is b
True
Do this instead:
MySecondList = MyList[:]
What this does is makes a copy of a list which won't change the original list. You can also use list(MyList).

Categories