Strange behaviour when changing value of list in a list - python

While coding on a python project I noticed a strange behavior when I tried to change the value of a list in another list.
Not working code:
lst = []
to_add = [None,None,None]
for i in range(3):
lst.append(to_add)
for i in range(3):
lst[i][0] = anotherslistoflists[i][0]
To my surprise this example gave unexpected results. To be specific, every lst[i][0] got assigned with the same value which was the first element from the last index of the "anotherlistoflists" list.
Result example (when printing lst)
("word1",None,None)
("word1",None,None)
("word1",None,None)
Working code:
lst = []
for i in range(5):
lst.append([None,None,None])
# same code as above
Result example2 (expected result)
("word1",None,None)
("word2",None,None)
("word3",None,None)
Of course, as you can see the problem is solved but I was wondering why the first code does not work. Something is probably wrong with the "to_add" list but I don't understand what is the problem here.

You are appending the same list, to_add, three times. Then, once you modify it, all three items which point to it will reflect the same change. If you want to create a new copy with the same values, you could use the built-in list function:
for i in range(3):
lst.append(list(to_add))

You are adding the same list, to_add to lst:
>>> a = [None]
>>> b = [a]
>>> c = [a]
>>> print a, b, c
[None] [[None]] [[None]]
>>> a[0] = 1
>>> print a, b, c
[1] [[1]] [[1]]

In the first case you are not adding a copy of add_to_list you are actually adding that exact list over and over, so when you change it later you're changing the real add_to_list and everywhere that refers to it. In the second case you're adding a new list containing None each time, and modifying different lists each time through

Related

Why can't a list be constructed and modified in the same line?

For example, why is a not equal to b?
a = [1]
a.append(2)
print(a) # [1, 2]
b = [1].append(2)
print(b) # None
The syntax for b doesn't look wrong to me, but it is. I want to write one-liners to define a list (e.g. using a generator expression) and then append elements, but all I get is None.
It's because:
append, extend, sort and more list function are all "in-place".
What does "in-place" mean? it means it modifies the original variable directly, some things you would need:
l = sorted(l)
To modify the list, but append already does that, so:
l.append(3)
Will modify l already, don't need:
l = l.append(3)
If you do:
l = [1].append(2)
Yes it will modify the list of [1], but it would be lost in memory somewhere inaccessible, whereas l will become None as we discovered above.
To make it not "in-place", without using append either do:
l = l + [2]
Or:
l = [*l, 2]
The one-liner for b does these steps:
Defines a list [1]
Appends 2 to the list in-place
Append has no return, so b = None
The same is true for all list methods that alter the list in-place without a return. These are all None:
c = [1].extend([2])
d = [2, 1].sort()
e = [1].insert(1, 2)
...
If you wanted a one-liner that is similar to your define and extend, you could do
c2 = [1, *[2]]
which you could use to combine two generator expressions.
All built-in methods under class 'List' in Python are just modifying the list 'in situ'. They only change the original list and return nothing.
The advantage is, you don't need to pass the object to the original variable every time you modify it. Meanwhile, you can't accumulatively call its methods in one line of code such as what is used in Javascript. Because Javascript always turns its objects into DOM, but Python not.

Python not referencing to same list

In code below:
a=[0,1]
b=a
for i in range(2):
for j in b:
a=a+[j]
why does a print as:
[0,1,0,1,0,1]
and b as:
[0,1]
However when executed on idle both lists change:
>>> c=[9,0]
>>> d=c
>>> d+=[7]
>>> c
[9, 0, 7]
Since a is being appended, why doesn't b change as is the property of python list assignment?
Since a is being appended why doesn't b change as is the property of python list assignment?
a is not appended. When you write:
a = a+[j]
you each time construct a list [j] and then construct a new list a+[j] that contains all the elements of a and then j.
Now you let a refer to the new list, but b still refers to the old list. Since the old list is not updated (the state is not altered, for instance through append), the list remains the same (which is good since iterating over a list you alter can have unwanted side effects).
If you would use a.append(j) or a += [j] instead of a = a + [j], then the list will be updated (in the latter case, you implicitly call a.extends([j])). Since both a and b refer to that list, b will thus also be updated. But mind that since we iterate over b at the same time, we could end up in an infinite loop. So you better do not do that anyway.
a is not appended. Appending is done with the append command like so:
a.append(1)
Every time you add (a + [j]) you construct a new object.

How to update a list of variables in python?

This is an aggravating issue that I've come into whilst programming in python. The user can append various variables to a list however, this list when accessed later on will still display the same results as when first assigned. e.g below
a=1
b=2
list = [a,b] #user defined order
print list # 1,2
a=2
print list # prints 1,2
I need list to print out 2,2. However i cannot find out a way to DYNAMICALLY update the list to accommodate ANY order of the variables (many ways are hard coding which i've seen online such as assigning list = [a,b] when needed to update however i dont know if its b,a or a,b)
Help would be much appreciated. Thank you
Edit : My question is different due to being about varaibles that need to be dynamically updated in a list rather than simply changing a list item.
you need to update the list and not the variable:
a = 1
b = 2
lst = [a, b] # the list now stores the references a and b point to
# changing a later on has no effect on this!
lst[0] = 2
and please do not use list as variable name! this is a keyword built-in type in python and overwriting it is a very bad idea!
if you only know the value of the element in the list (and this value is unique) you could do this:
old_val = 2
new_val = 3
lst[lst.index(old_val)] = new_val
It's not possible if you store immutable items in a list, which Python integers are, so add a level of indirection and use mutable lists:
a,b,c,d = [1],[2],[3],[4]
L = [d,b,a,c] # user-defined order
print L
a[0] = 5
print L
Output:
[[4], [2], [1], [3]]
[[4], [2], [5], [3]]
This has the feel of an X-Y Problem, however. Describing the problem you are solving with this solution may elicit better solutions.
Why don't you use dictionary??
With dictionary:
_dict = {}
_dict["a"] = 1
_dict["b"] = 2
print _dict["a"] # prints 1
Now if you want to set and get value of variable "a":
_dict["a"] = 2
print _dict["a"] # prints 2
At the assignment 'a=1' an integer object is created with the value 1. That object is added to your list. At the second assignment (a=2) a new object is created, but the old one still is in the list, its value unchanged. That is what you see.
To change this behaviour, the value of the object must be changed. Ints are immutable in Python, meaning that exactly this, i.e. changing the value, is not possible. You'll have to define your own and then change its value. For example:
class MyInt(object):
def __init__(self, value):
self.setValue(value)
def setValue(self, value):
self.value = value
def __repr__(self):
return str(self.value)
a=MyInt(1)
print(dir(a))
b=2
list = [a,b] #user defined order
print (list) # 1,2
a.setValue(2)
print (list) # prints 2,2
It is not clean but a work around trick , you can assign the value of a is a single element list
a = [1]
b = 2
So when you assign them to a list:
lst = [a,b]
and change the value of a by calling its 0th element:
a[0] = 2
it will also change the referenced list that is a
print(lst)
#[[2],2]
likewise if you place the value in the list:
lst[0][0] = 2
print(a[0])
#2
You can define a function:\
a, b = 1, 2
def list_():
return [a, b]
list_() # [1, 2]
a = 2
list_() # [2, 2]

Python - external variable being changed by function

I am trying to write a function that squares each value in a list, returning a new list with individual values squared. I pass a list to it and it changes the original list. I'd like it to not make any changes to the original list, so that I can use it in other functions.
def squareset(c):
d=c
count = len(c)
print(c)
for i in range(0,(count),1):
d[i]=d[i]**2
return d
test = [1,2,3]
print(squareset(test))
print(test)
I don't have this problem with functions operating on simple variables of type int or float.
I added the d=c line trying to prevent the change to the list test, but it makes no difference. print(test) is producing the result [1,4,9] instead of [1,2,3]. Why is this happening and how can I fix this?
Doing d=c simply makes the parameter d point to the same object that c is pointing to. Hence, every change made to d is made to the same object that c points to.
If you want to avoid changing c you'll have to either send a copy of the object, or make a copy of the object inside the function and use this copy.
For example, do:
d = [i for i in c]
or:
d = c[:]
instead of:
d = c
Assigning the list to another variable doesn't copy the list. To copy, just
def squareset(c):
d=c[:]
...
While the other answers provided are correct I would suggest using list comprehension to square your list.
In [4]:
test = [1,2,3]
results = [elm**2 for elm in test]
print test
print results
[1, 2, 3]
[1, 4, 9]
If you wanted a function:
def squareList(lst):
return [elm**2 for elm in lst]
Try this:
def square(var):
return [x*x for x in var]
x = [1,2,3]
z = square(x)

Iteratively add elements to a list

I am trying to add elements of a list in Python and thereby generate a list of lists. Suppose I have two lists a = [1,2] and b = [3,4,5]. How can I construct the following list:
c = [[1,2,3],[1,2,4],[1,2,5]] ?
In my futile attempts to generate c, I stumbled on an erroneous preconception of Python which I would like to describe in the following. I would be grateful for someone elaborating a bit on the conceptual question posed at the end of the paragraph. I tried (among other things) to generate c as follows:
c = []
for i in b:
temp = a
temp.extend([i])
c += [temp]
What puzzled me was that a seems to be overwritten by temp. Why does this happen? It seems that the = operator is used in the mathematical sense by Python but not as an assignment (in the sense of := in mathematics).
You are not creating a copy; temp = a merely makes temp reference the same list object. As a result, temp.extend([i]) extends the same list object a references:
>>> a = []
>>> temp = a
>>> temp.extend(['foo', 'bar'])
>>> a
['foo', 'bar']
>>> temp is a
True
You can build c with a list comprehension:
c = [a + [i] for i in b]
By concatenating instead of extending, you create a new list object each iteration.
You could instead also have made an actual copy of a with:
temp = a[:]
where the identity slice (slicing from beginning to end) creates a new list containing a shallow copy.

Categories