Why is this list changing value? - python

I have a list called ones that changes value after a block of code that shouldn't affect it. Why?
s = 3
ones = []
terms = []
for i in range (0, s):
ones.append(1)
terms.append(ones)
print(terms)
twos = []
if len(ones) > 1:
twos.append(ones)
twos[-1].pop()
twos[-1][-1] = 2
print(twos)
print(terms)
Output:
[[1, 1, 1]] # terms
[[1, 1, 2]] # twos
[1, 1, 2] # terms
For context, I'm trying to use this to begin to solve the problem on page 5 of this British Informatics Olympiad past exam: http://www.olympiad.org.uk/papers/2009/bio/bio09-exam.pdf.

Here:
twos.append(ones)
You are appending a reference to ones, not its values. See the difference:
In [1]: l1 = [1, 2, 3]
In [2]: l2 = []
In [3]: l2.append(l1)
In [4]: l2, l1
Out[4]: ([[1, 2, 3]], [1, 2, 3])
In [5]: l2[0][1] = 'test'
In [6]: l2, l1
Out[6]: ([[1, 'test', 3]], [1, 'test', 3])
In order to avoid this you can give a copy by using [:] operator:
In [7]: l1 = [1, 2, 3]
In [8]: l2 = []
In [9]: l2.append(l1[:])
In [10]: l2, l1
Out[10]: ([[1, 2, 3]], [1, 2, 3])
In [11]: l2[0][1] = 'test'
In [12]: l2, l1
Out[12]: ([[1, 'test', 3]], [1, 2, 3])

twos.append(ones) does not copy ones.
There is only ever one list ones in memory, which also goes by the following references:
terms[0]
twos[0]
and also terms[-1] and twos[-1] because terms and twos only have one element each, so the first is the last.
Now, when you mutate ones/terms[0]/terms[-1]/twos[0]/twos[-1] you are mutating the same list in memory.
I highly recommend watching Facts and Myths about Python names and values.

When you do twos.append(ones), you're passing the reference to the ones list, not the value itself. Therefore, when you do twos[-1][-1] = 2, it'll modify the value in the ones list itself, not a copy in the twos list.
To pass the value instead of the reference to the ones list, you can do:
twos.append(ones[:])

Related

Splitting arrays in Python

I have the following problem: I would like to find different "cuts" of the array into two different arrays by adding one element each time, for example:
If I have an array
a = [0,1,2,3]
The following splits are desired:
[0] [1,2,3]
[0,1] [2,3]
[0,1,2] [3]
In the past I had easier tasks so np.split() function was quite enough for me. How should I act in this particular case?
Many thanks in advance and apologies if this question was asked before.
Use slicing, more details : Understanding slicing.
a = [0,1,2,3]
for i in range(len(a)-1):
print(a[:i+1], a[i+1:])
Output:
[0] [1, 2, 3]
[0, 1] [2, 3]
[0, 1, 2] [3]
Check this out:
a = [0,1,2,3]
result = [(a[:x], a[x:]) for x in range(1, len(a))]
print(result)
# [([0], [1, 2, 3]), ([0, 1], [2, 3]), ([0, 1, 2], [3])]
# you can access result like normal list
print(result[0])
# ([0], [1, 2, 3])

How do we change the indexes of a list without changing the original list on python?

I'm trying to add 2 to the elements of a list without
changing the elements of the original list in python. I used the append method, but when I try to return the original is modified too.
It should work like this:
>> list1 = [1, 3, 5]
>> add_two(list1)
[3, 5, 7]
>> list1 == [1, 3, 5]
True
However this is what I got:
>> list1 = [1, 3, 5]
>> add_two(list1)
[3, 5, 7]
>> list1
[3, 5, 7]
Can anyone help me please ?
List is a mutable data type. If you want to manipulate it without changing the original, you should pass the copy of that list to the function add_two().
In [1]: def add_two(lst):
...: lst[2] += 2
...: return lst
...:
In [2]: a = [ 1,2,3]
In [3]: add_two(a)
Out[3]: [1, 2, 5]
In [4]: a
Out[4]: [1, 2, 5]
In [5]: add_two(a.copy())
Out[5]: [1, 2, 7]
In [6]: a
Out[6]: [1, 2, 5]
EDIT
I see you want to increase each list element by two.
Then it is easiest to use list comprehension - creates a new list, no need to pass a copy...
def add_two(lst):
return [v+2 for v in lst]

Python - Declare two variable with the same values at the same time

a=[1,2,3]
b=[1,2,3]
Is there a way to do this on one line? (obviously not with ";")
a,b=[1,2,3]
doesn't work because of
a,b,c=[1,2,3]
a=1
b=2
c=3
In [18]: a,b=[1,2,3],[1,2,3]
In [19]: a
Out[19]: [1, 2, 3]
In [20]: b
Out[20]: [1, 2, 3]
you may also want to do this:
In [22]: a=b=[1,2,3]
In [23]: a
Out[23]: [1, 2, 3]
In [24]: b
Out[24]: [1, 2, 3]
but be careful that, a is b is True in this case, namely, a is just a reference of b
a,b,c = [[1,2,3] for _ in range(3)]
each points to a different object
Edit: as found by DSM in order for the following lines to work you need to declare b as a list in order for my code to work (so this is no longer on one line, but I will leave it here as a reference). Changed the order as suggested by Paulo Bu
a=[1,2,3]
b=a[:]
Old code:
b=[]
a=b[:]=[1,2,3]
This assigns the values to b and then copies all the values from b to a.
If you do it like this:
a=b=[1,2,3]
and then change
b[1] = 0
a would also be changed
>>> a = b = [1, 2, 3]
>>> a
[1, 2, 3]
>>> b
[1, 2, 3]
>>> b = [3, 2, 1]
>>> b
[3, 2, 1]
>>> a
[1, 2, 3]
a,b = [1,2,3],[1,2,3] does it in one line and points to different objects.
a=b=[1,2,3] is clear but points to the same object.
Try a.pop(1) in both cases and you will see the difference.
Note... this solution
a=b=[1,2,3]
results in assigning the same (mutable) list to ivar's a and b. The original question shows 2 different lists being assigned to a & b.
This solution suffers the same problem:
>>> a,b = ([1,2,3],)*2
>>> a
[1, 2, 3]
>>> b
[1, 2, 3]
>>> b.append(4)
>>> b
[1, 2, 3, 4]
>>> a
[1, 2, 3, 4]

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]

Why this Python expression doesn't produce expected result?

This code snippet:
a = [3, 2, 1]
a.sort()
produces [1, 2, 3] list. Why
[3, 2, 1].sort()
doesn't produce the same result? Is there a "one-liner" to accomplish this?
You can use
sorted_array = sorted([2,3,1])
>>> [3, 2, 1].sort()
It does sort this list object but as the number of references(variables pointing) to this list are 0, so it is garbage collected.(i.e we can't access this list anymore)
Coming to your first question, list.sort() sorts a list in-place and returns None.
>>> a = [3, 2, 1]
>>> a.sort()
>>> a
[1, 2, 3]
If you want a to stay as it is, use sorted and assign the result back to some variable.
>>> a = [3, 2, 1]
>>> b = sorted(a)
>>> b
[1, 2, 3]
>>> a
[3, 2, 1]
list.sort is a method that sorts an existing list therefore returning None
sorted is a builtin function that creates a new sorted list from its input and returns it.

Categories