How to copy list elements in python - python

Regarding the code below:
A = [[1, 2], [3, 4]]
A[0] = A[1]
B = A[:][0]
B[0] = 5
print(A)
print(B)
I'm wondering why printing B gives [5, 4].
I thought that B = A[:][0] is the same as A[0][0], A[1][0], which would then be [3, 3]. Then, B[0] = 5, so it would print [5, 3].
Could someone kindly clear up my confusion?

after this line:
A[0] = A[1]
A = [[3,4],[3,4]]
A[:]
will return A itself, so when you access 0, you will get [3,4]
B = A[:][0]
and after putting 5, B = [5,4]

Here is your confusion:
I thought that B = A[:][0] is the same as A[0][0], A[1][0]
It's not. The : symbol in Python isn't used for that sort of shorthand like in some other languages. A[:] simply returns a copy of A, so A[:][0] is equivalent to A[0], which in this context is [3, 4].
(The more general use of the : is for an operation called slicing, which is when you copy only part of a list, from a given starting index to a given end index. If we omit the indices, they default to the start and end of the entire list, so we copy the whole thing.)

Using the copy() method is relatively straightforward.
a = [[1, 2], [3, 4]]
b = a.copy()
print(a)
print(b)
result
[[1, 2], [3, 4]]
[[1, 2], [3, 4]]
If you want to make copies of the nested lists, then you would extend to this:
a = [[1, 2], [3, 4]]
b = [a[0].copy(), a[1].copy()]

Related

Creating a list of mutable items repeated N times

I have a list containing a set of x mutable items. I would like to create another list where the set of x mutable items is repeated n times. However, the items must be references to unique objects rather than simply references to the original objects.
For example, let a = [[1],[2],[3]]. Suppose I would like the numbers inside to be repeated n=3 times, i.e. [[0],[2],[3],[1],[2],[3],[1],[2],[3]].
I can do this easily by using a * 3. The problem is that if I change a[0][0] = 0, then I will get a == [[0],[2],[3],[0],[2],[3],[0],[2],[3]], which is not desirable because I would like to change only the first element. Is there any other way to achieve this?
itertools has a method for this called repeat. Just pair it with chain.
from itertools import repeat, chain
a = [1,2,3]
b = list(chain(*repeat(a, 3)))
>>> b
[1,2,3,1,2,3,1,2,3]
a = [1,2,3]
a = a*3
a[0] =0
print(a)
Very simply, you need to make copies of a, rather than repeating references to the original, mutable object. If you need just one level of copy, you can do it like this, noting that it's a shallow copy (see second output).
a = [[1, 1], [2], [3, 3, 3]]
b = a[:] * 3
b[0] = "change"
print(b)
b[2][1] = "deep"
print(b)
Output:
['change', [2], [3, 3, 3], [1, 1], [2], [3, 3, 3], [1, 1], [2], [3, 3, 3]]
['change', [2], [3, 'deep', 3], [1, 1], [2], [3, 'deep', 3], [1, 1], [2], [3, 'deep', 3]]
See how only the first level is given a new reference. When you try to change a deeper element, you see that the nested lists are still the same object. To fix that problem, use a deep copy for each element of b:
import copy
a = [[1, 1], [2], [3, 3, 3]]
b = [copy.deepcopy(a) for _ in range(3)]
b[0] = "change"
print(b)
b[2][1] = "deep"
print(b)
Output:
['change', [[1, 1], [2], [3, 3, 3]], [[1, 1], [2], [3, 3, 3]]]
['change', [[1, 1], [2], [3, 3, 3]], [[1, 1], 'deep', [3, 3, 3]]]
Just do:
n = [sub_a.copy() for sub_a in a] * 3
You need copy() to avoid the problem.
a = [[1],[2],[3]]
n = [sub_a.copy() for sub_a in a] * 3
a[0] = [-1]
a[2][0] = -1
print (n)
Output:
[[1], [2], [3], [1], [2], [3], [1], [2], [3]]
To get new instances of mutable objects, you can map the object type's copy() method to the multiplied list:
a = [{1},{2},{3}]
b = [*map(set.copy,a*3)]
a[0].add(7)
b[0].add(8)
b[3].add(9)
print(a) # [{1, 7}, {2}, {3}]
print(b) # [{8, 1}, {2}, {3}, {1, 9}, {2}, {3}, {1}, {2}, {3}]
If you don't know the type of the items, or if they have different types, you can do it in a list comprehension:
b = [ i.copy() for i in a*3 ]
If not all of the items are mutable, you can do it like this:
b = [type(i)(i) for i in a*3]
If some can be None ...
b = [i if i is None else type(i)(i) for i in a*3]
You can also use deepcopy which covers all these cases including objects that contain nested objects ...
from copy import deepcopy
b = [*map(deepcopy,a*3)]
I just figured out I can do this through:
from copy import deepcopy
a = [[1],[2],[3]]
n = 3
b = [deepcopy(val) for _ in range(n) for val in a]
Now if I set b[0][0] = 0, I will get b == [[0],[2],[3],[1],[2],[3],[1],[2],[3]].

Why doesn't the copy of a list in a function work?

b = [0]
def copyalist(b):
b = [1, 2, 3]
print(b)
copyalist(b)
print(b)
The outputs are below:
[1, 2, 3]
[0]
The first line indicates that in the function, b was set to [1, 2, 3];
However, when you print(b) out of the function,the second output says that b is still [0].
I don't understand that, why the outer b is not changed?
I also tried b = copy.deepcopy([1, 2, 3]), the outputs are the same.
However, the following code works well:
b = [0]
def copyalist(b):
b += [1, 2, 3]
print(b)
copyalist(b)
print(b)
The outputs are below:
[0, 1, 2, 3]
[0, 1, 2, 3]
This code:
def copyalist(b):
b = [1, 2, 3]
print(b)
will only mean to remap the variable name b to a new list, but not modifying the original b. If you mean to modify the original list, you have to explicitly tell Python to replace the actual content. The way to do is:
def copyalist(b):
b[:] = [1, 2, 3]
print(b)
In python, lists are passed as function arguments only by reference, i.e., only the memory address of the first element is given. When defining a new b inside the function, you just change the position in memory to which the inner variable b refers, but the outer b still points to the original position. Vice versa, when you do b += [1, 2, 3], you change the content inside the cell referenced by the inner b and, since inner and outer b point to the same cells, it reflects in a change of the outer b as well.
This is due to a difference in global and local variables. You can add global to your function to get the desired result.
b = [0]
def copyalist():
global b
b = [1, 2, 3]
print(b)
copyalist()
print(b)
OUTPUT
[1, 2, 3]
[1, 2, 3]
A more in depth summary is here
b = copyalist(b)
and return b in the function
def copyalist(b):
b = [1, 2, 3]
return(b)
When you are defining something in a function it is only used inside that function.

How to insert a list at a specific index?

I got a list
a=[1,2,3]
and a list of list
b=[[1,2],[3,4,5]]
and I want to insert a into b at index 1 so b becomes
b=[[1,2],[1,2,3],[3,4,5]]
How do I do that?If I use insert it won't work because I can only insert an item not a list?
EDIT:I realised insert can be used for lists as well.Thanks.
You can use list.insert which takes the index as the first argument
>>> a=[1,2,3]
>>> b=[[1,2],[3,4,5]]
>>> b.insert(1, a)
>>> b
[[1, 2], [1, 2, 3], [3, 4, 5]]
You can use list slicing:
b=[[1,2],[3,4,5]]
a = [1, 2, 3]
final_list = b[:1]+[a]+b[1:]
Output:
[[1, 2], [1, 2, 3], [3, 4, 5]]

python [:] notation and range

I've been tasked with converting some python code over to Java. I came across some notation I'm not familiar with and can't seem to find any information on. I'm guessing this is a lack of keywords on my part.
I've sanitized the code and hard coded some basic values for simplicity.
index_type = c_int * 1000 #size of int, basically 1000 integers?
indexes = index_type() # not entirely sure what this does
indexes[:] = range(2000, 3000)[:] # no idea
# c_int equals 4
The logic doesn't really matter to me, I'm just trying to figure out what's going on in terms of datatypes and converting to Java.
This is called "slicing". See the tutorial sections on Strings and Lists for a good description of how it works. (In your example, it's actually a ctypes array that's being sliced, not a list, but they work the same way. So, for simplicity, let's talk about lists.)
The notation indexes[:] is a slice of the entire list. So, you can do this:
a = [1, 2, 3]
b = a[:]
… to get a copy of the whole list, and this:
a[:] = [4, 5, 6]
… to replace the contents of the whole list.
You may wonder how this is different from just using a itself. The difference is that in the first case, b = a doesn't copy the list, it just makes another reference to the same list, and in the second case, a = [4, 5, 6] doesn't mutate the list, it rebinds a to refer to a new list. Here's an example that shows the difference:
>>> a = [1, 2, 3]
>>> b = a # b is now the same list as a
>>> a[0] = 10 # so changing that list is visible to b
>>> b
[10, 2, 3]
>>> a = [1, 2, 3]
>>> b = a[:] # b is now a new list, with a copy of a
>>> a[0] = 10 # so changing the original list doesn't affect b
>>> b
[1, 2, 3]
>>> a = [1, 2, 3]
>>> b = a # b is now the same list as a
>>> a = [4, 5] # but now a is a different list
>>> a[0] = 10
>>> b
[1, 2, 3]
>>> a = [1, 2, 3]
>>> b = a # b is now the same list as a
>>> a[:] = [4, 5] # and we've replaced the contents
>>> b
[4, 5]
You may wonder why anyone would use range(2000, 3000)[:] on the right side of the assignment.
Well, I wonder the same thing.
If this is Python 2.x, range(2000, 3000) is a brand-new list. You replace the contents of indexes with the contents of that range list, then give up your only reference to the range list. There is no way anyone could possibly end up sharing it, so there is no good reason to make an extra copy of it, unless you're worried that your computer has too much CPU and too much RAM and might be getting bored.
If this is Python 3.x, range(2000, 3000) is a smart range object. And range(2000, 3000)[:] is a new and equal range object. The copying this time is a lot cheaper, but it's exactly as unnecessary.
The other answers are talking about what x[:] means as an expression. But when this notation is used as the target of an assignment (i.e., on the left side of the =), it means something different. It is still a slice of the object, but it doesn't create a copy; rather, it assigns the given value (the right hand side) to the specified "part" of the object. If you use [:], the slice is the whole object, so its contents will be replaced by what you pass.
An example:
>>> x = [1, 2, 3]
>>> x[:] = [8, 8]
>>> x
[8, 8]
Notice that you can replace the contents by new contents of different length.
If you use a partial slice, only part of the contents will be replaced:
>>> x = [1, 2, 3, 4]
>>> x[1:3] = [8, 8, 88]
>>> x
[1, 8, 8, 88, 4]
The notation:
[a:b]
Is a range that starts at a and ends at b. When you leave one of them blank, it counts as "beginning" and "end", respectively.
Basically, your indexes is a list of values, each one has a number to associate with it that shows its order inside indexes. It starts at 0 and progresses until the end. The line of code:
indexes[:]
Simply means
"first value of indexes to last value of indexes"
I hope this helps,
happy coding!
It means getting a COPY of the original list, since lists are mutable in python, for example
>>a = [1,2,3]
>>b = a
>>a[0] = 3
>>a
[3, 2, 3]
>>b
[3, 2, 3]
>>b = a[:]
>>a[0] = 0
>>a
[0, 2, 3]
>>b
[3, 2, 3]
[:] is an example of slice notation, Python's way to specify any number of items in a sequence. [:] specifies the entire sequence. The notation is succinct and powerful in what it can express. Some other possible forms are:
sequence[n] #an index to one item (not a slice)
sequence[n:] #the nth item to the end of the sequence
sequence[:n] #all items until the nth item (exclusive)
sequence[m:n] #the mth item until the nth item (exclusive)
sequence[:] #all items
sequence[::2] #every other item
sequence[::-1] #all items in reverse order
When slicing is used to modify items in a sequence, the item(s) are modified:
>>> a = [1, 2, 3]
>>> a[0:1] = [4] #same as a[0] = 4
>>> a
[4, 2, 3]
>>> a[:] = [1, 2, 3, 4] #the sequence can be made larger
>>> a
[1, 2, 3, 4]
>>> a[:] = [] #or smaller
>>> a
[]
When slicing is used to read items in a sequence, a shallow copy of the item(s) are returned instead of a reference to the item. The term shallow copy means that if a copied item is itself a sequence, the item will refer to the original sequence, not a copy of it:
For instance:
>>> a = [1, 2, 3]
>>> b = a[0]
>>> b #b has the same value as, but does not reference the same object as a[0]
1 #this is a copy of a[0]
>>> a = [1, 2, 3]
>>> b = a[:]
>>> b #b has the same values as, but does not reference the same object as a
[1, 2, 3] #this is a copy of list a
>>> a = [1, 2, 3]
>>> b = [a, 2, 3]
>>> c = b[:] #c is a hallow copy of b
>>> c
[[1, 2, 3], 2, 3]
>>> b[:] = [] # modifying b does not affect c
>>> c
[[1, 2, 3], 2, 3]
>>> a[0] = 4 # modifying a will affect a in c
>>> c
[[4, 2, 3], 2, 3]

Slice away first element in each (list) element in a list in Python

Say we have a list:
a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Then I want to create a new list through slicing away the first element in each element in the list:
b = a[][1:]
Obviously the above doesn't work, but what I want b to be now is:
[[2, 3], [5, 6], [8, 9]]
Of course I can just loop through it, but all I want to know is if it's possible to do it in any similar way of what I tried so wrongly to do above. Also, preferably is there a better/good way of doing this with numpy, in which case it doesn't need to be done through slicing the way I attempted to do it?
You can use list comprehensions:
b = [x[1:] for x in a]
Demo:
>>> a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> b = [x[1:] for x in a]
>>> b
[[2, 3], [5, 6], [8, 9]]
>>>
Using numpy indexing/slicing notation, you use commas to delimit the slice for each dimension:
import numpy as np
a = np.array([[1,2,3],[1,2,3],[1,2,3]])
print a[:,1:]
output:
[[2 3]
[2 3]
[2 3]]
For additional reading on numpy indexing:
http://docs.scipy.org/doc/numpy/reference/arrays.indexing.html
in python 3 you can also use *
b = [x for _,*x in a]
this approach is more flexible since you can for example left first and last elements of the inside list, no matter how long is the list:
b = [first,last for first,*middle,last in a]

Categories