Python Append vs list+list - python

I read Python list + list vs. list.append(), which is a similar question, but my question is more in relation to the code below
a = [[]] * 4
b = [[]] * 4
a[3] = a[3] + [1]
b[3].append(1)
print a, b
Which gives:
[[],[],[],[1]] [[1],[1],[1],[1]]
Why would these 2 be any different? I've never run into an example like this where these 2 methods have different outputs...
Thanks

a[3] = a[3] + [1] is not modifying a[3]. Instead, it is putting a new item there. a[3] + [1] creates a list that is just like a[3] except that it has an extra one at the end. Then, a[3] = ... sets a at the index 3 to that new list.
b[3].append(1) accesses b[3] and uses its .append() method. The .append() method works on the list itself and puts a one at the end of the list. Since [[]] * 4 creates a list with four copies of another list, the .append() method reveals its changes in all items of b.

Related

Why does Python list comprehension seem to behave differently than list "multiplication"? [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 8 months ago.
Asking out of curiosity. For the sake of making a point I was trying to make a function that returns an "identity matrix" of n dimensions and then printing it in the most concise way.
First I came up with this:
def identity(n):
zeros = [[0 for j in range(n)] for i in range(n)]
for i in range(n):
zeros[i][i] = 1
return zeros
for i in range(5):
print(identity(5)[i])
This works as intended, however then I tried making the syntax shorter by doing this:
def identity(n):
zeros = [[0]*n]*n
for i in range(n):
zeros[i][i] = 1
return zeros
for i in range(5):
print(identity(5)[i])
And this for some reason changes every single element to a one, but I can't seem to figure out why?. This isn't an important question but help is much appreciated!
lists are kept by reference in python.
This means that if you have:
list_a = [1,2,3,4]
list_b = list_a
list_a and list_b are actually pointing to the same object in memory. so if you change an element of list_a:
list_a[2] = 9
then the same element for list_b will change because they are pointing to the same object. i.e. list_a literally equals list_b in every way.
That's what's happening in your code as well.
When you loop through and assign each value then it's as if you were explicitly creating a new list and assigning it to each element of your outter list:
l = []
l.append([1,2,3,4])
l.append([1,2,3,4])
...
but in the second piece of code, it is as if you are repeatedly appending the same value to the list:
l = []
la = [1,2,3,4]
l.append(la)
l.append(la)
It's because list comprehension performs shallow copy on the element.
All elements in zeros are refering to the same list.
Try these and see the results:
n = 4
zeros = [[0]*n]*n
zeros[0] = 1
print(zeros)
n = 4
lst = [0]*n
zeros = [lst]*n
print(zeros)
lst[0] = 1
print(zeros)
You can learn more about difference between shallow and deep copy here.
This is because when you multiply a list with a number, it duplicates it, but it does so by copying the reference of each element.
If you do id(zeros[0][0]) and id(zeros[0][1]) for the second case, you will see that they are the same, i.e. they refer to the same element and modifying one modifies them all.
This is not true for the first case where every 0 is instantiated seperately and has its own id.
Edit:
def zeros(n):
return [[0]*n]*n
x = zeros(5)
for i in range(5):
for j in range(5):
assert id(x[0][0]) == id(x[i][j])

How to change values in a list while using for loop in Python? (without using index)

I'm trying to take two lists and use one list to make changes in the second list as the following code shows:
a = [1,2,3,4,5]
b = [0,0,0,0,0]
for aA, bB in zip(a,b):
bB = aA*4
print(b)
I get the result b = [0,0,0,0,0],
I want b = [4,8,12,16,20]
I know that you shouldn't modify containers while iterating with a for loop. But is there any work-around in python to be able to get the intended result of modifying a container?
Python unlike many other languages is dynamically typed as in you don't need to initialize prepopulated arrays(lists) before populating them with data. Also a standard convention is list comprehensions which in lamens are in-line loops that return a list.
a = [1,2,3,4,5]
b = [num * 4 for num in a]
This is equivilent to:
a = [1,2,3,4,5]
b = []
for num in a:
b.append(num * 4)
Please try this:
b.append( aA*4)
Why not use index?
for i in range(len(a)):
b[i] = a[i]
The standard advice is "don't change a list while you're iterating over it". We say this because if you add or remove an element then the iterator will start doing weird hard-to-predict things. But that's not what's happening here: we're iterating over a and changing b, knowing in advance they're the same size, so there's no undefined or hard-to-understand behavior.
The values you want to put into b only depend on a, so there is no reason to initialize and iterate on b. You could do either:
# build list b as you go
b = []
for aA in a:
b.append(aA*4)
print(b)
or
# use a list comprehension
b = [aA*4 for aA in a]
print(b)

".append" and "+" semantic difference inside of functions

I'm having some trouble to understand what is the difference between these 3 examples.
#example1
list1 = [1,2,3,4]
list1= list1+[6]
list1.append(1000)
print("Example 1: ", list1)
# example 2
def f(j):
j= j + [6]
j.append(1000)
list2 = [1,2,3,4]
f(list2)
print("Example 2: ", list2)
# example 3
def f(j):
j.append(1000)
j= j +[6]
list3 = [1,2,3,4]
f(list3)
print("Example 2: ", list3)
Output:
The first one I did some simple addition using (+) and (.append), it worked fine.
The second one I created a function. I guess I understood the results. In my opinion, it remained the same because the changes that I've done in the original list were only made locally, so, after the function had finished, the original list have remained the same. Am I right?
The third one I can't understand. Because it is exactly like the second one, I've just changed the order of the elements, however the output is completely different.
Example 2 creates a new list with j = j + [6], and the only reference to the that list is the local variable j, so the change is not visible after f returns.
Example 3 appends the value to the original list referenced by j, which list3 still refers to.

Python [0]*n syntax works only on immutable object?

a = [[0]*3] * 4
print(a[0] is a[1]) # True
when I initialize a two dimensional array that way, things went wrong.
Took me a bit time to find this unexpected behavior. So is this syntax only work on immutable object?
It "worked" in your example too, in its way. This is just how the implementation of list.__mul__ interprets what you want. The list can't construct new objects, it doesn't know how to create new objects from whatever objects it happens to contain. It expands itself with new references to those objects.
You get the same behavior with immutable integers
>>> x = [0] * 3
>>> x[0] is x[1]
True
You can get the two dimensional array with
>>> a = [[0]*3 for _ in range(4)]
>>> a[0] is a[1]
False
The reason why [0] * 3 does what you want is that it is creating list that contains 3 references to the same immutable 0.
What went"wrong"?
It does make a two-dimensional list. (I don't know if that's a thing but I mean to say it makes the list in a form where you can convert it to a 2-D array).
To convert it to a proper array, you will have to use
import numpy as np
a = [[0]*3] * 4
a = np.array(a)
You can make this immutable by converting it to a tuple.

How to completely delete list items from memory

I need a way to completely delete list items from memory.
The point is that even if an object is in more than one list, when I delete it it will no longer be available in any list and it will be completely deleted from memory.
I tried del, but it doesn't work:
a = [Object(), Object()]
b = a[1]
del a[1]
but the value of a [1] is still in memory and b != None
How do I delete it completely from memory?
In Python, assignment operator binds the result of the right-hand side expression to the name from the left-hand side expression.
So when you define this
a = [Object(), Object()]
b = a[1]
del a[1]
a is an object and the b is another object. When you are defining b = a[1] it gives the same reference since the values are the same. but when you are deleting the a[1] simply it disconnect the connection between the a[1] and the value. but still, b is connected to that value so it remains.
Link that visualizes evverything

Categories

Resources