difference between list and array(np)? [duplicate] - python

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 8 years ago.
I was trying add ones to some specific position in a 100*100 zero matrix. My code was something this:
adjacency=[[0]*100]*100
while ...
x=...
y=...
adjacency[x][y]=adjacency[x][y]+1
But it adds to other positions as well. In face, adjacency[0]=adjacency[1]=..=adjacency[99], while there's no way that the calculation of x, y in while-loop will produce this kind of result.
So, I changed to array. The only thing I changed was:
adjacency=np.zeros((100,100))
And the result is right this time. Adjacency[i] no longer equals to each other.
Does anyone know why? (Using Python2.7.)

The * operator is not always a good thing for creating lists, specially multi-dimensional lists. This operator just creates copies of the elements in the list to populate the larger list. This works fine for primitive types, but not for other objects.
For example, when you write [0] * 100, it creates a list of 100 elements and assigns 0 to each item. Now you have the reference of a 100-element list. So when you write [[0] * 100] * 100], you get a list of 100 references of the list [0] * 100. So when you change a value, it affects all the 'rows', because all the rows are referencing the same list.
Here's a smaller example:
>>> a = [0] * 3
>>> print a
[0, 0, 0]
>>> b = [a] * 2
>>> print b
[[0, 0, 0], [0, 0, 0]]
>>> a[0] = 1
>>> print a
[1, 0, 0]
>>> print b
[[1, 0, 0], [1, 0, 0]]
>>> b[0][1] = 2
>>> print b
[[1, 2, 0], [1, 2, 0]]

The first row list is copied every time to the 99 other positions. You need to create a new list for each row, something like this:
adjacency=[[0] * 100 for y in range(100)]

Related

Python list generation pitfall [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 1 year ago.
Attempt 1
>>> width = 3
>>> height = 2
>>> zeros = [[0]*width]*height
>>> print(zeros)
[[0, 0, 0], [0, 0, 0]]
>>> zeros[1][2] = "foo"
>>> print(zeros)
[[0, 0, 'foo'], [0, 0, 'foo']]
Expected result was [[0, 0, 0], [0, 0, 'foo']]. It seems that the * operator populated each row with references to the same object, so modifying zeros[1] also modified zeros[0].
Attempt 2
So now we create the initial list object using list comprehension instead:
>>> zeros = [[0 for i in range(width)] for j in range(height)]
>>> print(zeros)
[[0, 0, 0], [0, 0, 0]]
>>> zeros[1][2] = "foo"
>>> print(zeros)
[[0, 0, 0], [0, 0, 'foo']]
Great, this is the expected result.
The question is...
If the * operator populates each row with references to the same object, then why doesn't every element within each row refer to the same object?
That is, with a (partial) understanding of why Attempt 1 didn't produce the expected output, I would expect that if ANY element of zeros is modified, ALL elements would be changed, that is:
>>> zeros = [[0]*width]*height
>>> print(zeros)
[[0, 0, 0], [0, 0, 0]]
>>> zeros[1][2] = "foo"
>>> print(zeros)
is expected to return:
>>> print(zeros)
[['foo', 'foo', 'foo'], ['foo', 'foo', 'foo']]
but instead returns:
>>> print(zeros)
[[0, 0, 'foo'], [0, 0, 'foo']]
So why does the second * operator populate each row with references to the same object, but each of the elements WITHIN any given row are unique objects?
Answer
List of lists changes reflected across sublists unexpectedly
tl;dr:
Integers are immutable, so [0]*3 creates copies the elements of [0], not references. However [0,0,0] is an object, so [0,0,0]*2 creates a new reference to the original object.
The fundamental disconnect here is that you believe:
zeros[1][2] = "foo"
Is *mutating the element in the rows. It isn't. It is mutating the list at zeros[1], which happens to be the same list at zeros[0].
You can't mutate int objects. Although, they are, in fact, the same int objects:
>>> zeros = [[0]*2]*2
>>> zeros
[[0, 0], [0, 0]]
>>> [[id(x) for x in zs] for zs in zeros]
[[4413403792, 4413403792], [4413403792, 4413403792]]
So, using int objects might actually obscure this, since you might have heard that small integers are cached in CPython. They are always the same object. But consider a user-defined class (which is mutable):
>>> from dataclasses import dataclass
>>> #dataclass
... class Foo:
... foo: int
...
>>> fs = [[Foo(0)]*2]*3
>>> fs
[[Foo(foo=0), Foo(foo=0)], [Foo(foo=0), Foo(foo=0)], [Foo(foo=0), Foo(foo=0)]]
>>> fs[0][0].foo = 8
>>> fs
[[Foo(foo=8), Foo(foo=8)], [Foo(foo=8), Foo(foo=8)], [Foo(foo=8), Foo(foo=8)]]
So note, with the zeros example, there is no method we can call:
zeros[0][0].some_method()
Such that the int object at zeros[0][0] would be mutated, as would happen in the equivalent case of fs[0][0].foo = 8, because int objects don't expose mutator methods. That is what immutable means by definition.

Replace some None in list without affecting other None [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 4 years ago.
I just met something really strange of Python:
>>> out=[[0]*3]*3
>>> out
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> out[0][1]
0
>>> out[0][1]=9
>>> out
[[0, 9, 0], [0, 9, 0], [0, 9, 0]]
well, obviously, what I want is :
[[0, 9, 0], [0, 0, 0], [0, 0, 0]]
isn't strange? I'm not very familiar with Python, but Python always impresses me with its intuitive behavior. But how it comes up with this?
... and how can I get what I need?
thanks!
Watt
A strange behaviour indeed, but that's only because * operator makes shallow copies, in your case - shallow copies of [0, 0, 0] list. You can use the id() function to make sure that these internal lists are actually the same:
out=[[0]*3]*3
id(out[0])
>>> 140503648365240
id(out[1])
>>> 140503648365240
id(out[2])
>>> 140503648365240
Comprehensions can be used to create different lists as follows:
out = [ [0]*3 for _ in range(3) ]
Using * to duplicate elements in lists is a shallow copy operation, so you will end up with multiple references to the same mutable objects if you use this on a list that contains mutable objects.
Instead, use the following to initialize your nested list:
out = [[0]*3 for _ in range(3)]
You can see that with your method, each entry in out is actually a reference to the same list, which is why you see the behavior that you do:
>>> out = [[0]*3]*3
>>> out[0] is out[1] is out[2]
True

Initializing a two-dimensional array in Python? [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 6 years ago.
I wonder What is the difference between this two different method of 2D matrix initialization in python.
m =[]
K = []
for x in range(0,W+1):
m.append(0)
for x in range(0,n+1):
K.append(m)
and
l = []
for j in range(0,n+1):
l.append([])
for i in range(0,W+1):
l[j].append(0))
when I tried to print l and K the both gave the same answer but when I tried to implement it in code the output of program changed.
the earlier one(K) gave an incorrect answer but later one(l) when implemented in the program gave a correct answer.
In the first one, you are storing the same reference to list m in k. Change in m will be reflected in all the reference.
Where as in second, you are creating new lists in l. Hence, change in one is independent of another.
Alternatively, simpler way to initialize list is as:
Holding same reference to list (similar to your first approach) is as:
>>> row, columns = 2, 3
>>> my_list = [[0]*columns]*row
>>> my_list
[[0, 0, 0], [0, 0, 0]]
>>> my_list[0][1] = 1 # changing value
>>> my_list
[[0, 1, 0], [0, 1, 0]]
# ^ ^ Reflected in all
Holding different reference to the list (similar to your second approach) is as:
>>> my_list = [[0]*columns for i in range(row)]
>>> my_list
[[0, 0, 0], [0, 0, 0]]
>>> my_list[0][1] = 1 # changing value
>>> my_list
[[0, 1, 0], [0, 0, 0]]
# ^ only one value is changed

Understanding Python List operation

I am new to python. i am learning some basic stuff. I was doing some operation on python list like this three_lists=[]*3 when i execute this piece of code it gives me only one empty list like this[]. Why it is not giving me 3 empty list? some what like this [],[],[]
It says right in the Python docs
s * n or n * s equivalent to adding s to itself n times
where s is a sequence and n is an int. For example
>>> [1,2,3]*3
[1, 2, 3, 1, 2, 3, 1, 2, 3]
This is consistent with other sequences as well, such as str
>>> 'hello'*3
'hellohellohello'
If you wanted a list of 3 empty lists you could say
>>> [[] for _ in range(3)]
[[], [], []]
it doesn't multiply lists it's multiply items inside the list
for example
>>> x = []*3
>>> x
[]
>>> y = [1]*3
>>> y
[1, 1, 1]
The rules of arithmetic(operators in programming) still apply, you can't multiple with '0' or empty value and get something different, you get the original value that you had before.

python - Add a number to an integer inside a nested list [duplicate]

This question already has answers here:
Abnormal list behaviour
(2 answers)
Closed 9 years ago.
This is MWE of what I'm trying to do:
lis = []
# Initialize empty list
for i in range(2):
lis.append([[0]]*2)
# First print
print lis
# Second print
print lis[0][1][0]
# Modify item
lis[0][1][0] += 1
# Third print
print lis
The first print returns the list as [[[0], [0]], [[0], [0]]] which is correct, I have a first list which is composed of several lists, each one also composed of several lists (I need this nested lists for what I'm doing). The second print returns the item in indexes 0 and 1 as 0 which is also correct. But the third print shows me the new list as:
[[[1], [1]], [[0], [0]]]
instead of:
[[[0], [1]], [[0], [0]]]
which is what I actually aimed at. What am I doing wrong?
That is happening because both inner lists are actually copies of same object, so modifying one will affect the other one as well.
>>> l = [[0]]*2
>>> [id(x) for x in l]
[145328716, 145328716] #same IDs
>>> l[0].append(4)
>>> l
[[0, 4], [0, 4]]
Use a list comprehension to avoid that:
>>> l = [[0] for _ in xrange(2)]
>>> [id(x) for x in l] #Different IDs
[145327372, 145327500]
>>> l[0].append(4)
>>> l
[[0, 4], [0]]

Categories

Resources