Python operator asterisk in list [duplicate] - python

This question already has answers here:
Creating a new list from elements of an other list, referencing the elements of the latter
(2 answers)
Closed 2 years ago.
Why the output here is [[2, 1], [2, 1]] and not [[2,1],[1,1]]
a = [[1] * 2] * 2
a[0][0]=2
print (a)
I have done quite research on the * operator in list but it doesn't seem to get me to the correct understanding

It's due to the fact, how Python handles objects in memory. When you are using the * 2 to duplicate the list, Python does not create a separate list object in memory, it only copies the memory pointer for the second list position. Due to this behaviour when you mutate the first element, it affects the second too.
You can use the id() function to check the object ids within Python. You will see that a[0] and a[1] will have the same ids.
>>> a = [[1] * 2] * 2
>>> a[0][0] = 2
>>> a
[[2, 1], [2, 1]]
>>> id(a[0])
140608840898432
>>> id(a[1])
140608840898432
>>> id(a[0]) == id(a[1])
True

When you do this, you are just creating copies of list, n times.
So the inner lists here are actually the same list. When you modify the first one, you're also modifying the second. If you don't want that functionality, you can do this instead:
a = [[1 for _ in range(2)] for _ in range(2)]

Related

Having problem with Shared Reference in case of Lists [duplicate]

This question already has answers here:
Is there a difference between "==" and "is"?
(13 answers)
Closed 2 years ago.
a=b=[1,2,3]
print (a is b) #True
But
a=[1,2,3]
print (a is [1,2,3]) #False
Why does the second part print False ?
Multiple assignment in Python creates two names that point to the same object. For example,
>>> a=b=[1,2,3]
>>> a[0] = 10
>>> b
[10, 2, 3]
is can be used to check whether two names (a and b) hold the reference to the same memory location (object). Therefore,
a=b=[1,2,3] # a and b hold the same reference
print (a is b) # True
Now in this example,
a = [1,2,3]
print (a is [1,2,3]) # False
a does not hold the same reference to the object [1, 2, 3], even though a and [1, 2, 3] are lists with identical elements.
In case you want to compare whether two lists contain the same elements, you can use ==:
>>> a=b=[1, 2, 3]
>>> a == b
True
>>>
>>> a = [1, 2, 3]
>>> a == [1, 2, 3]
True
Your first one explicitly makes a and b references to the object created by the list display [1,2,3].
In your second code, both uses of the list display [1,2,3] necessarily create new list objects, because lists are mutable and you don't want to implicitly share references to them.
Consider a simpler example:
a = []
b = []
a.append(1)
Do you want b to be modified as well?
For immutable values, like ints, the language implementation may cause literals to reuse references to existing objects, but it's not something that can be relied on.
the problem is the logic operator you are using.
You are asking are these identical object with is and not if they are the equal (same data).
One is a reference to a object and the other is the object so even though they are equal the are not the same.
Why your results
When you are setting a and b as the same list you are saying that a and b should be linked and should reference the same data so they are identical to each other but a and b are not the object [1,2,3] they are a reference to a list that is the same.
In summary
== - equal to (same).
is - identical to.
So if you want to check if they are equal(same) use:
>>> a=[1,2,3]
>>> print (a == [1,2,3])
True
Similar question worth reading:
Is there a difference between "==" and "is"?
Hope this helps, Harry.

Understanding python syntax for lists [duplicate]

This question already has answers here:
Understanding slicing
(38 answers)
Closed 5 years ago.
list = sorted(set(list))
list[:] = sorted(set(list))
list[::] = sorted(set(list))
I am new to Python, and the first thing I am noticing is that the syntax is concise, but non obvious.
For example, it is not clear what is going on in the three statements above. I ran them and got some results and seems like statement 1 is not updating the list, while statement 2 and statement 3 are. But, I am sure there is more going on here.
What do each of the above assignments mean?
2 and 3 do the same (the step argument of a slice is optional, and both these slices use the default step of 1), but they both are inherently different from 1. Slice assignment (lst[:] = ...) mutates the original object while a common assignment (lst = ...) rebinds the variable to a new object.
>>> lst = [3,3,2,2,1,1]
>>> id(lst)
139793325565704
>>> lst[:] = sorted(set(lst))
>>> lst
[1, 2, 3]
>>> id(lst)
139793325565704 # same object
>>> lst = sorted(set(lst))
>>> id(lst)
139793325524744 # different object
A point worth noting is that slice assignment can have any iterable on the rhs (for partial slices their number of elements must match the length of the slice):
>>> lst = [1,2,3]
>>> lst[1:] = 'ab'
>>> lst
[1, 'a', 'b']
See some of the slice docs for more detailed information.

Ids of Mutable objects in a Python List [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
How can I initialize a list of lists to a certain size?
(2 answers)
Closed 6 years ago.
I have been using the [Value] * Count notation in Python for initializing a list. For eg., [False] * 3 results in creation of list [False, False False]. I tried using the same notation for initializing a list of lists.
>>>a = [[0] * 2] * 3
>>>print a
[[0, 0], [0, 0], [0, 0]]
>>>a[0][1] = 23
>>>print a
[[0, 23], [0, 23], [0, 23]]
>>>id(a[0])
139978350226680
>>>id(a[1])
139978350226680
>>>id(a[2])
139978350226680
As we can see, the elements of a refer to a single list thrice instead of referring to three different lists.
1. Why does this happen?
2. What is the correct way to initialize a list of lists?
The same behavior has been pointed out in an answer previously: https://stackoverflow.com/a/13382804/4716199
That's the behavior of the * operator on a list: it does not make copies, but copies the reference. That makes sense since it is in general not even defined how to make copies, so Python can only copy the reference since it is the only thing every object has.
In case you want to construct new lists, you can use list comprehension:
a = [[0] * 2 for _ in range(3)]
In general it is not good practice to use * given the list contains mutable objects.
For immutable objects like bool and str this works since you do not work on the object itself, but on the resulting array. In you example you used chained indexing, so you modify the inner list.

Array inside an array in Python [duplicate]

This question already has an answer here:
Strange behavior of lists in python [duplicate]
(1 answer)
Closed 8 years ago.
This is not really a question about an error. Rather, a clarification on how things work.
I am putting an element(0 for example) inside an array which is also inside an array. Doing it iteratively, the code looks like this:
arr = [[0 for x in range(2)] for x in range(2)]
Recursively,
def zero(array,n,i):
if i >= n:
return array
else:
array[i].append(0)
return zero(array,n,i+1)
arr = [[]] * 2
print zero(arr,2,0)
They would both have an output like this:
[[0,0],[0,0]]
I did not get the process that the recursion underwent(I just accidentally made that code through trial and error.).
What I did not get was since I am appending zero in the first array(with index zero) inside the bigger array, shouldn't '0' be appended only in the first array and not on the second array? But that is not the case, '0' was appended to both array. And when it returns the function, instead of appending '0' to the second array, it was appended to both array again.
I tried to trace it but I really don't understand how it happened. Thank you for the response.
This would be because
arr = [[]] * 2
is creating another reference to the same list construct. So you have an array with two elements both of which are a reference to the same list. Thus an application on one of them, such as appending an element, will cause that same operation to be reciprocated to the other.
Breaking down the constituent parts of the list comprehension using the interactive interpreter:
>>> arr = [[0 for x in range(2)] for x in range(2)]
>>> print arr
[[0, 0], [0, 0]]
>>> inner_array = [0 for x in range(2)]
>>> print inner_array
[0, 0]
>>> total_arr = [inner_array for x in range(2)]
>>> print total_arr
[[0, 0], [0, 0]]
So you have an array of [0,0], and then place that in the outer list comprehension which creates that same array twice, due to the range(2) component.
One can verify that they are the exact same array as follows:
>>> id(inner_array)
139834121190864
>>> id(total_arr[0])
139834121190864
>>> id(total_arr[1])
139834121190864
id returns the address of the object, and here, they are all the same.

Deepcopy on nested referenced lists created by list multiplication does not work

As much as I love Python, the reference and deepcopy stuff sometimes freaks me out.
Why does deepcopy not work here:
>>> import copy
>>> a = 2*[2*[0]]
>>> a
[[0, 0], [0, 0]]
>>> b = copy.deepcopy(a)
>>> b[0][0] = 1
>>> b
[[1, 0], [1, 0]] #should be: [[1, 0], [0, 1]]
>>>
I am using a numpy array as a workarround which I need later on anyway. But I really had hoped that if I used deepcopy I would not have to chase any unintended references any more. Are there any more traps where it does not work?
It doesn't work because you are creating an array with two references to the same array.
An alternative approach is:
[[0]*2 for i in range(2)]
Or the more explicit:
[[0 for j in range(2)] for i in range(2)]
This works because it creates a new array on each iteration.
Are there any more traps where it does not work?
Any time you have an array containing references you should be careful. For example [Foo()] * 2 is not the same as [Foo() for i in range(2)]. In the first case only one object is constructed and the array contains two references to it. In the second case, two separate objects are constructed.
It works exactly as you have expected.
a = 2*[2*[0]]
When you multiply [[0,0]] with 2 *, both elements of the new list will point to the SAME [0,0] list. a[0] and a[1] are the same list, because the reference is copied, not the data (which would be impossible). Changing the first element of one of them changes the first element of the other.
copy.deepcopy copies the list correctly, preserving unique objects.

Categories

Resources