I have two lists: a = [0], b = [[0,1]], I want to append 2 to a first, then append a to b. So b should be [[0,1], [0,2]].
Operations like this work well:
a.append(2)
b.append(a)
but when I tried to combine them:
b.append(a.append(2))
I got the results:
a = [0, 2], b = [[0, 1], None]
What's wrong here?
As answered in the comments, a.append(2) only appends 2 to the list a, but it doesn't actually return it. An append operation that doesn't modify the original list but returns a new list with the appended value can be written simply with the +-operator.
>>> a, b = [0], [[0, 1]]
>>> b.append(a + [2]) # The list that is returned to the append function is [0, 2]
>>> a
[0]
>>> b
[[0, 1], [0, 2]]
I think the comments already answered your question: the append() method modifies the list in place instead of creating a new one, and its return type is None. If you still want to do the operation in a single line, you could use an assignment expression:
a, b = [0], [[0, 1]]
b.append(a := a + [2])
print(a, b)
# [0, 2] [[0, 1], [0, 2]]
If you want to combine those you might make your own function,
def append_ret(x, value):
x.append(value)
return x
doing the operation and returning the 'appended' container.
append_ret(b, append_ret(a,1))
Related
I have a nested list, and I need to reverse every element in the list.
Below is the example :
L = [[0, 1, 2], [1, 2, 3]]
Expected Output :
L = [[3, 2, 1], [2, 1, 0]]
I tried with the below piece of code, it works individually but when I am putting this code within function, then the list is not getting updated.
L = [list(reversed(row)) for row in L]
L.reverse()
This code works, and the List "L" is getting updated.
But when I put this code in function
def deep_rev(L):
L = [list(reversed(row)) for row in L]
L.reverse()
L = [[0, 1, 2], [1, 2, 3]]
deep_rev(L)
print(L)
This is returning the same list = [[0,1,2],[1,2,3]]
Can anyone please help me in this regard, why in function this is not working?
With L = ... you are assigning a new value to the parameter L within the function, without modifying the original list. Instead, you can use L[:] = ... to replace all the elements in the original list with the values from the new list.
def deep_rev(L):
L[:] = [list(reversed(row)) for row in reversed(L)]
L = [[0, 1, 2], [1, 2, 3]]
deep_rev(L)
print(L) # [[3, 2, 1], [2, 1, 0]]
Your current code creates a new list, rather than modifying the exiting list in place. You can make it work, just get rid of the list comprehension and do in-place reversals for the inner lists too:
def deep_rev(L):
for inner in L:
inner.reverse()
L.reverse()
If you want to support more than two levels of nesting, you could recursively call deep_rev on the inner lists, rather than just reversing them as I did above. You'd need to check if the values were lists or not, so you'd have a base case.
This is the solution I find out using recursion:
l=[[0, 1, 2], [1, 2, 3]]
def treereverse(l):
l.reverse()
for x in l:
if isinstance(x,list):
treereverse(x)
return l
print(treereverse(l))
This Code works for your problem,
L = [[0, 1, 2], [1, 2, 3]]
b=list(reversed(L))
q=[]
for i in b:
q.append(list(reversed(i)))
print q
Output:-
[[3, 2, 1], [2, 1, 0]]
def deep_rev(l):
l = [list(reversed(row)) for row in l]
l.reverse()
return l
l = [[0, 1, 2], [1, 2, 3]]
l = deep_rev(l)
print(l)
output
[[3, 2, 1], [2, 1, 0]]
deep_rev(L) modifies one local list, its scope is inside this function. Either make one deep copy of the list or return the reversed list.
I am working on recursion and I have a problem that I have solved but I would like to know what is going on and why the way I tried first does not work. The only issue I have is emptying the listOfX when it has a length of 3.
Here is the original code that does not work as expected:
sol = []
def recLoop(levelWidth,levelOn,listOfX):
if len(listOfX) == 3:
sol.append([listOfX[0],listOfX[1]])
sol.append([listOfX[0],listOfX[2]])
listOfX = [] #THIS DOES NOT WORK
print listOfX
if len(levelWidth) != levelOn:
for x in range(levelWidth[levelOn]):
listOfX.append(x)
recLoop(levelWidth,levelOn+1,listOfX)
recLoop([3,2],0,[])
print sol
However it works as expected when I use pop() instead like this:
sol = []
def recLoop(levelWidth,levelOn,listOfX):
if len(listOfX) == 3:
sol.append([listOfX[0],listOfX[1]])
sol.append([listOfX[0],listOfX[2]])
listOfX.pop()
listOfX.pop()
listOfX.pop()
print listOfX
if len(levelWidth) != levelOn:
for x in range(levelWidth[levelOn]):
listOfX.append(x)
recLoop(levelWidth,levelOn+1,listOfX)
recLoop([3,2],0,[])
print sol
NOTE: the outcome of the first code is:
[[0, 0], [0, 1]]
but it should be:
[[0, 0], [0, 1], [1, 0], [1, 1], [2, 0], [2, 1]]
The statement:
listOfX = []
simply rebinds the local variable (which is just a reference to an object) to a new list. The original list object is still referenced elsewhere but not affected by this rebinding. Using listOfX.pop() on the other hand directly manipulates that object.
You can use:
listOfX[:] = []
to clear the list, instead.
I define this function to do: [1,2,3] --> [2,3,1]
def shift_to_left(p):
p.append(p[0])
return p[1:]
When I check like this, results are ok:
p1 = [1,2,3]
print p1
p1 = shift_to_left(p1)
print p1
The result:
[1, 2, 3]
[2, 3, 1]
However, when I introduce another list and concatenate as I go the result is different:
ss = []
p1 = [1,2,3]
ss.append(p1)
p1 = shift_to_left(p1)
ss.append(p1)
print ss
The result
[[1, 2, 3, 1], [2, 3, 1]]
But I want:
[1,2,3]
[2,3,1]
why is it happening?
Thanks very much,
In Python, most arguments are taken by reference.
Your function, shift_to_left, actually mutates its argument (through the use of append), but then returns a slice (which is a shallow copy of the list).
When you replace your original variable with the output of shift_to_left, this behaviour is hidden:
In [1]: def shift_to_left(p):
...: p.append(p[0])
...: return p[1:]
...:
In [2]: xs = [1, 2, 3]
In [3]: xs = shift_to_left(xs)
In [4]: xs
Out[4]: [2, 3, 1]
But if we instead assign the result into a new variable, we can see that the original list has indeed been changed:
In [5]: ys = shift_to_left(xs)
In [6]: ys
Out[6]: [3, 1, 2]
In [7]: xs
Out[7]: [2, 3, 1, 2]
Our result, ys, is the slice of xs from the second element onwards. That's what you expected.
But xs itself has also been changed by the call to append: it's now one element longer than before.
This is what you're experiencing in your second example.
If you do not want this behaviour, one way of avoiding it is by passing a copy of your list to shift_to_left:
In [8]: zs = shift_to_left(ys[:])
In [9]: zs
Out[9]: [1, 2, 3]
In [10]: ys
Out[10]: [3, 1, 2]
Here, you can see that the original list ys has not been modified, as shift_to_left was given a copy of it, not the object itself. (This is still passing by reference, of course; it's just not a reference to ys).
Alternatively, and probably more reasonably, you could change shift_to_left itself, so that it does not modify its arguments:
def shift_to_left(xs):
return xs[1:] + xs[0] # build a new list in the return statement
The big problem with both these approaches is that they create lots of copies of lists, which can be incredibly slow (and use a lot of memory) when the lists are large.
Of course, as #Marcin points out, if this is more than an academic exercise, you should probably use one of the built-in data structures such as deque.
If you want to shift/rotate elements in a list, I think better would be to use a deque, rather than reinvent the wheel. For example:
from collections import deque
d = deque([1,2,3])
d.rotate(-1)
print(d)
#[2, 3, 1]
If you run your code here, you can notice that ss remains pointing to the original (mutated in your shift function because of p.append(p[0])) copy of p1, where as p1 points to a knew list all together when it gets reassigned, resulting in the behavior. (Step 10 out of 11)
(p becomes mutated, and ss[0] = p)
(p1 gets assigned to a new list altogether, which is latter appended to ss)
why is it happening?
return p[1:] is "non-destructive": it creates a new list. However, p.append(p[0]) is "destructive": it changes p itself.
First you append p1 to ss. This makes [[1, 2, 3]], where [1, 2, 3] is p1.
Then you do your shift_to_left, which changes p1 to [1, 2, 3, 1] and returns [2, 3, 1]. Because p1 is contained in ss, your ss becomes [[1, 2, 3, 1]], and then you append the new p1 to form [[1, 2, 3, 1], [2, 3, 1]].
A better implementation would be purely non-destructive:
def shift_to_left(p):
return p[1:] + [p[0]]
Try this:
p1 = [1,2,3]
p1 = shift_to_left(p1)
ss = []
ss.extend(p1)
print ss
That prints [2, 3, 1]. Use extend() instead because append() will create an array in an array. Also you had an extra call to ss.append(p1).
I'm trying to write some code to manipulate a list in something like the following manner:
>>> a = [[0,0],[0,1],[0,2]]
>>> b = a[1]
>>> b[1] = 3
>>> a
[[0,0],[0,3],[0,2]]
>>> # So far so good
>>> del b
>>> a
[[0,0],[0,3],[0,2]]
>>> # Huh.
How can I delete an item from the list using a variable pointing to that item?
EDIT:
OK so it seems this operation is impossible in Python, which seems a shame considering it parallels the pythonic way to iterate over a list. The equivalent operation in C would be very simple to do via pointers. Closing this question without assigning a right answer.
No you can't do that.
del is just unbind the var name from the instance object. If you wanna del an element in a list by index, use pop(index):
In [46]: a=range(4)
In [47]: a.pop(2)
Out[47]: 2
In [48]: a
Out[48]: [0, 1, 3]
or del a[index]:
In [50]: del a[2] #this time it deletes "3" in list "a"
In [51]: a
Out[51]: [0, 1]
Quoting from the del docs,
Deletion of a name removes the binding of that name from the local or
global namespace, depending on whether the name occurs in a global
statement in the same code block. If the name is unbound, a NameError
exception will be raised.
So, you are actually unlinking the name b from the current namespace, not removing the data from the list.
Deletion of attribute references, subscriptions and slicings is passed
to the primary object involved; deletion of a slicing is in general
equivalent to assignment of an empty slice of the right type (but even
this is determined by the sliced object).
So, you should be doing
del a[1]
This is the proper way to do delete the element.
Edit: The only foolproof way to do this would be, this
c = [0, 0]
a = [c, c, [0, 2]]
b = a[0]
for idx in xrange(len(a) - 1, -1, -1):
if a[idx] is b:
del a[idx]
print a # [[0, 2]]
Or you can keep removing the item from the list, with .remove until it raises an error, like this
try:
while True:
a.remove(b)
except ValueError, e:
pass
print a
Is this what you want?
>>> a
[[0, 0], [0, 1], [0, 2]]
>>> del b[:]
>>> a
[[0, 0], [], [0, 2]]
If you don't know the index, try following. (used is operator to test for object identity).
>>> c = [0, 0]
>>> a = [c, c, [0, 2]]
>>> b = a[0]
>>> for j in reversed([i for i, x in enumerate(a) if x is b]): # find indice.
... del a[j]
...
>>> a
[[0, 2]]
You are looking for a.remove(b).
Is the basic documentation for python lists
I cannot figure out at all why this is happening:
A = [[1,0], [2,2]]
B = list(A)
print('start A:', A, 'start B:', B)
A[0][0] = 999
print('end A:', A, 'end B:', B)
This returns:
start A: [[1, 0], [2, 2]] start B: [[1, 0], [2, 2]]
end A: [[999, 0], [2, 2]] end B: [[999, 0], [2, 2]]
The lists A and B end up being the same, even though I explicitly copied B from A. This only happens when I do something like A[0][0] = 999; if I replace that with A[0] = 999 then A and B are different at the end.
What's the reason behind this, and is there any way to change A in this manner without affecting B?
You are creating a shallow copy of the original list, that is a new list containing new references to the same objects as the original list.
Modifying the new list object does not alter the original list. Modifying the objects in the new list does modify the objects in the old list because they are the same.
To get a completely separate list, use copy.deepcopy() to create a deep copy.
Both A and B contain the same two lists.
Your code is roughly equivalent to this:
x = [1, 0]
y = [2, 2]
A = [x, y]
B = [x, y]
The operation A[0][0] = 999 is effectively just doing x[0] = 999. That is, it doesn't modify A itself, it modifies the first element of the list x. Since both A and B have references to x, both will see the change.
A simple copy operation like you did is shallow, it only copies the items one level deep and does not recurse into nested structures. You need
>>> import copy
>>> A = [[1,0], [2,2]]
>>> B = copy.deepcopy(A)
>>> print('start A:', A, 'start B:', B)
start A: [[1, 0], [2, 2]] start B: [[1, 0], [2, 2]]
>>> A[0][0] = 999
>>> print('end A:', A, 'end B:', B)
end A: [[999, 0], [2, 2]] end B: [[1, 0], [2, 2]]
A and B are two different names for the same chunk of memory within your computer.
A and B are two separate list objects, but A[0] and B[0] are two different names for the same chunk of memory within your computer. Try the following from the interpreter:
id(B)
id(A)
id(B[0])
id(A[0])
Python code manipulates references to objects.
Assigning to a variable is just binding a name to refer to an object.
A list consists of a bunch of references to objects. list(A) finds all the objects referenced in A and makes a new list with references to all the same objects. So if A is a list of lists, list(A) makes a new list with references to the same lists that were in A. So changing any of the sub-lists will be visible from both A and the new list.
copy.deepcopy exists to help you get around this, when you need a full "deep" copy of something.
Once you learn to think about Python code as manipulating references to objects like this, you will intuitively understand when code is likely to end up referring to the same object from multiple places like this, though there will probably always be obscure cases that surprise you.