Add an object to a python list - python

I am trying to add an object to a list but since I'm adding the actual object when I try to reset the list thereafter, all the values in the list are reset.
Is there an actual way how I can add a monitor object to the list and change the values and not affect the ones I've already saved in the list?
Thanks
Code:
arrayList = []
for x in allValues:
result = model(x)
arrayList.append(wM)
wM.reset()
where wM is a monitor class - which is being calculated / worked out in the model method

Is your problem similar to this:
l = [[0]] * 4
l[0][0] += 1
print l # prints "[[1], [1], [1], [1]]"
If so, you simply need to copy the objects when you store them:
import copy
l = [copy.copy(x) for x in [[0]] * 4]
l[0][0] += 1
print l # prints "[[1], [0], [0], [0]]"
The objects in question should implement a __copy__ method to copy objects. See the documentation for copy. You may also be interested in copy.deepcopy, which is there as well.
EDIT: Here's the problem:
arrayList = []
for x in allValues:
result = model(x)
arrayList.append(wM) # appends the wM object to the list
wM.reset() # clears the wM object
You need to append a copy:
import copy
arrayList = []
for x in allValues:
result = model(x)
arrayList.append(copy.copy(wM)) # appends a copy to the list
wM.reset() # clears the wM object
But I'm still confused as to where wM is coming from. Won't you just be copying the same wM object over and over, except clearing it after the first time so all the rest will be empty? Or does model() modify the wM (which sounds like a terrible design flaw to me)? And why are you throwing away result?

You need to create a copy of the list before you modify its contents. A quick shortcut to duplicate a list is this:
mylist[:]
Example:
>>> first = [1,2,3]
>>> second = first[:]
>>> second.append(4)
>>> first
[1, 2, 3]
>>> second
[1, 2, 3, 4]
And to show the default behavior that would modify the orignal list (since a name in Python is just a reference to the underlying object):
>>> first = [1,2,3]
>>> second = first
>>> second.append(4)
>>> first
[1, 2, 3, 4]
>>> second
[1, 2, 3, 4]
Note that this only works for lists. If you need to duplicate the contents of a dictionary, you must use copy.deepcopy() as suggested by others.

while you should show how your code looks like that gives the problem, i think this scenario is very common. See copy/deepcopy

If i am correct in believing that you are adding a variable to the array but when you change that variable outside of the array, it also changes inside the array but you don't want it to then it is a really simple solution.
When you are saving the variable to the array you should turn it into a string by simply putting str(variablename). For example:
array.append(str(variablename))
Using this method your code should look like this:
arrayList = []
for x in allValues:
result = model(x)
arrayList.append(str(wM)) #this is the only line that is changed.
wM.reset()

Related

How to delete element ONLY from new list

Let suppose I have a list l and I make new list temp from l. When I try to delete element from temp it also deletes element from l too. This is so strange. How can I only delete element from temp?
l = [1,2,3]
temp = l
temp.remove(3)
print(l, temp)
Output: ([1, 2], [1, 2])
Actually both l and temp are names that refer to the same object list in memory. When you do something with any of them, you are doing it to the underlying object list that is pointed by those two names.
You can always create a new object by using the copy method:
temp = l.copy()
And that way, you will be having two names pointing to different objects.
When you create a list using temp=l, python doesn't create new object.
It refers to the same object as temp so whatever changes you make will reflect in original list as well. Use copy method to avoid this
l = [1,2,3]
temp = l.copy()
temp.remove(3)
print(l, temp)
Output:
[1, 2, 3] [1, 2]
If you do this:
l = [1,2,3]
temp = l
You haven't created a new list, but instead you have merely created a new reference to the same list. Changing the list through either variable changes it for both.
To copy the list, you can do:
temp = list(l)
Another way is to do temp = l[:], which is equivalent. In Python 3 you can also do temp = l.copy(), but that won't work in Python 2.
Then you can change either list without affecting the other.
temp = l is simply assigning temp to a reference of l. So whatever you do to one list, the other is affected.
We can check this with the id() function:
print(id(l))
# 1739514774400
print(id(temp))
# 1739514774400
Which returns the same unique address for each object in memory.
We can see this in the documentation:
Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.
CPython implementation detail: This is the address of the object in memory.
In this case both l and temp are the same, so they have the same identities.
We can fix this by assigning a copy instead:
l = [1,2,3]
temp = l[:]
Which now gives different identities and only removes 2 from temp:
print(id(l))
# 1822815159168
print(id(temp))
# 1822815159104
temp.remove(3)
print(l, temp)
# [1, 2, 3] [1, 2]

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 list mutation after copying

def mutation(input_list):
list_copy = input_list[:]
list_copy[0] = 10
input_list = list_copy
# Correctly mutates
sample_list = [0,1,2]
sample_copy = sample_list[:]
sample_copy[0] = 10
sample_list = sample_copy
print(sample_list)
# Incorrectly mutates
sample_list = [0,1,2]
mutation(sample_list)
print(sample_list)
In the top snippet of code, I've made a copy of a list and modified it. I then set the original to the copy and then it works.
What confuses me is why doing this process outside of a function works but if I were to do it inside a function (the 2nd snippet of code), it fails?
For reference, the code returns:
[10, 1, 2]
[0, 1, 2]
EDIT: I know that calling input_list[0] = 10 works. I just want to know what makes this different from what I showed above all in memory?
In mutation, input_list starts out pointing at the same object as sample_list, but later you make it point at list_copy.
sample_list is not modified. It is still pointing at the original object.
When you do it outside of the function you change sample_list to point to the new object before printing it.
I think that using the built-in function id to show the object ID will help here. If the ID of two variable names gives the same result then they refer to the same object; otherwise the objects are different.
>>> def mutation(input_list):
... print(id(input_list))
... list_copy = input_list[:]
... print(id(list_copy))
... input_list = list_copy
... print(id(input_list))
...
>>> a = list(range(10))
>>> print(id(a))
140737233394376
>>> mutation(a)
140737233394376
140737233289160
140737233289160
In the above, we see that after input_list = list_copy, the name input_list refers to identically the same object in memory as list_copy, which means it no longer refers to the list given as the function argument. This is why the mutation you expect does not work - you are modifying an entirely different object.
That's because you sets new value for input_list which is local variable of mutation function.
The simplest solution is changing value of first element of list passed as argument:
def mutation(input_list):
input_list[0] = 10
Otherwise you can write function which changes value of global variable called sample_list
def mutation():
global sample_list
list_copy = sample_list[:]
list_copy[0] = 10
sample_list = list_copy

What does this notation do for lists in Python: "someList[:]"?

I sometimes get across this way of printing or returning a list - someList[:].
I don't see why people use it, as it returns the full list.
Why not simply write someList, whithout the [:] part?
[:] creates a slice, usually used to get just a part of a list. Without any minimum/maximum index given, it creates a copy of the entire list. Here's a Python session demonstrating it:
>>> a = [1,2,3]
>>> b1 = a
>>> b2 = a[:]
>>> b1.append(50)
>>> b2.append(51)
>>> a
[1, 2, 3, 50]
>>> b1
[1, 2, 3, 50]
>>> b2
[1, 2, 3, 51]
Note how appending to b1 also appended the value to a. Appending to b2 however did not modify a, i.e. b2 is a copy.
In python, when you do a = b, a doesn't take the value of b, but references the same value referenced by b. To see this, make:
>>> a = {'Test': 42}
>>> b = a
>>> b['Test'] = 24
What is now the value of a?
>>> a['Test']
24
It's similar with lists, so we must find a way to really copy a list, and not make a reference to it. One way could be to recreate the list copy = list(list1), or use the functions of the copy module. But, after all, the easiest way, the prettiest, the best way ( ;) ) for doing this, is to copy each value of the first list to the other, by doing copy = list1[:]. It uses the slices, here list1 is sliced from index 0 to index len(list1), so the whole list1 is returned!
Moreover, the slice method is slightly faster: using the time.clock() method to measure the mean execution time of 1000 assignment of lists, each one containing 10000 random integers, with slices, constructor and deepcopy, the results show that the slices are 15% faster than the constructor method, and deepcopy is 4 times slower. However, this gain of time is negligible while using small lists: thus, using copy = list(list_to_copy) or copy = list_to_copy[:] is up to the developer's preferences.
Finally, we often forget the list.copy method, which seems to be the faster! In fact, it's even 13% faster than the slice method!
To create a copy of a list instead of passing by reference, as Python does. Use next two example to understand the difference.
Example:
# Passing by reference
SomeListA = [1, 2, 3]
SomeListB = [2, 3, 4]
SomeListB = SomeListA
SomeListA[2] = 5
print SomeListB
print SomeListA
# Using slice
SomeListA = [1, 2, 3]
SomeListB = [2, 3, 4]
SomeListB = SomeListA[:]
SomeListA[2] = 5
print SomeListB
print SomeListA
When you need to modify the list and you don't want to change the list and create another list you use
y = [1,2,3]
x = y[:]
and you can do a lot of changes to the list but the origin list will be in (y) and the modified in (x)
#Here is a simpler way for beginners to understand:
list16 = [1,2,3,4,5,6]
list17 = list16[:]
#^Identifying that 'list17' is a copy of 'list16' and not list16 directly
list17[0] = 10
#^ Making an alteration in the new copied list
print(list17)
print(list16)
= [10,2,3,4,5,6]
= [1,2,3,4,5,6]
#Printing the lists so that you can see what is happening. I created a copy of the list and altered it without changing the original list at all.
There are 2 copies available. 1) Deep Copy 2) Shallow Copy.
1) Deep Copy is you just copy the values
list = ['abc',123,'xyz']
list1 = copy.deepcopy(list) or list1 = list[:]
2) Shallow Copy is you just reference to the varible
list2 = copy.copy(list) or list2 = list
When you modify something on list2 it get effected in list also as it is referenced.
list1.append(456)
list2.append('789')
print "list: %s" %list
print "list1: %s" %list1
print "list2: %s" %list2
ans:
list : ['abc',123,'xyz','789']
list1 : ['abc',123,'xyz',456]
list2 : ['abc',123,'xyz','789']

Python short cut to fill list

Java has Arrays.fill(A,1). For a pre-existing List A, is there a shortcut for filling the list with 1? I am writing a function that takes an array and changes the array in some ways. Since arrays are pointers, my function will not return an array. The caller will see the changes after my function returns. The first step in my function is to fill the array with 1s. Doing
def my_work(A):
A =[1]*len(A)
# more work on A
does not seem to change A when my_work is done.
So is my only option
for i in range(len(A)):
A[i]=1
or is there a shortcut? Mine looks like a workaround.
If you really want to change A in place, the A[:] syntax should work:
>>> A = [1,2,3]
>>> def my_work(A):
... A[:] = [1]*len(A)
...
>>> A
[1, 2, 3]
>>> my_work(A)
>>> A
[1, 1, 1]
And here is the relevant section of the tutorial ("assignment to slices").
A is not changing because your not returning it our of my_work. Once my_work is complete the A you have in there is not referenced.
def my_work(A):
A =[1]*len(A)
return A #This line is needed
A = my_work(A)
Another way to do this is something like this.
def reset_list(L, x):
return [x for i in xrange(len(L))]
A = reset_list(A,1)

Categories