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]
Related
This question already has answers here:
Why can a function modify some arguments as perceived by the caller, but not others?
(13 answers)
How do I pass a variable by reference?
(39 answers)
Reassigning variables in python [duplicate]
(1 answer)
Closed 1 year ago.
So, here is my snippet. Here when I pass my original list (temp) to add_more. temp get updated.
temp = [1, 2, 3]
def add_more(l):
l.append(5)
add_more(temp)
print(temp)
But here when I do this:
temp = [1, 2, 3]
def add_more(l):
l = []
add_more(temp)
print(temp)
but here temp remain same as [1, 2, 3] and is not an empty list why?
In the first example, you manipulate the list you have passed to the function. but, in the second one you assign an empty list to the label "l", as a result, the list you have passed to the function, does not change.
In the first example, you have passed the reference of temp in the function. The .append method then adds the new element to it. append mutates the list and the reference of the list passed is mutated
In the second example, the parameter l is over written by the assignment expression l = []creating a variable l local to the function only. So temp remains unchanged.
This is related to How do I pass a variable by reference?
In short, list as a parameter is mutable.
In your first snippet, l points to what temp points to, and thus modification of l is also performed for temp.
In your second snippet, l = [] points l to this new empty list, while temp still points to the original one.
Hope this answers your question!
Because append is a mutable operation, and when the code inside the function l.append(5) is executed, it mutates the list l, in second case, when you are l=[] that's creation of an empty list, not the modification of the list passed.
Moreover, you can use id to verify it
First approach
temp = [1, 2, 3]
def add_more(l):
print(f'id of l:{id(l)} before')
l.append(5)
print(f'id of l:{id(l)} after')
add_more(temp)
print(f'id of temp: {id(temp)}')
id of l:1605604079816 before
id of l:1605604079816 after
id of temp: 1605604079816
Second approach
temp = [1, 2, 3]
def add_more(l):
print(f'id of l:{id(l)} before')
l = []
print(f'id of l:{id(l)} after')
add_more(temp)
print(f'id of temp: {id(temp)}')
id of l:1605677713736 before
id of l:1605604079816 after
id of temp: 1605677713736
In the first approach id, that is essentially the memory representation of a variable, is same for temp and before and after inside the function.
But in second case, id is same for temp and before in the function, but not after because you are just creating an empty list and assigning it to l, which is just a variable local inside the function.
l is not the list. It is a reference to that list.
If you want to change the value of the list in a function (rather than the value of the parameter that refers to the list), you must use slicing:
temp = [1, 2, 3]
def add_more(l):
l[:] = []
add_more(temp)
print(temp)
#[]
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.
This question already has answers here:
Different ways of deleting lists
(6 answers)
Closed 7 years ago.
Please what is the most efficient way of emptying a list?
I have a list called a = [1,2,3]. To delete the content of the list I usually write a = [ ]. I came across a function in python called del. I want to know if there is a difference between del a [:] and what I use.
There is a difference, and it has to do with whether that list is referenced from multiple places/names.
>>> a = [1, 2, 3]
>>> b = a
>>> del a[:]
>>> print(b)
[]
>>> a = [1, 2, 3]
>>> b = a
>>> a = []
>>> print(b)
[1, 2, 3]
Using del a[:] clears the existing list, which means anywhere it's referenced will become an empty list.
Using a = [] sets a to point to a new empty list, which means that other places the original list is referenced will remain non-empty.
The key to understanding here is to realize that when you assign something to a variable, it just makes that name point to a thing. Things can have multiple names, and changing what a name points to doesn't change the thing itself.
This can probably best be shown:
>>> a = [1, 2, 3]
>>> id(a)
45556280
>>> del a[:]
>>> id(a)
45556280
>>> b = [4, 5, 6]
>>> id(b)
45556680
>>> b = []
>>> id(b)
45556320
When you do a[:] you are referring to all elements within the list "assigned" to a. The del statement removes references to objects. So, doing del a[:] is saying "remove all references to objects from within the list assigned to a". The list itself has not changed. We can see this with the id function, which gives us a number representing an object in memory. The id of the list before using del and after remains the same, indicating the same list object is assigned to a.
On the other hand, when we assign a non-empty list to b and then assign a new empty list to b, the id changes. This is because we have actually moved the b reference from the existing [4, 5, 6] list to the new [] list.
Beyond just the identity of the objects you are dealing with, there are other things to be aware of:
>>> a = [1, 2, 3]
>>> b = a
>>> del a[:]
>>> print a
[]
>>> print b
[]
Both b and a refer to the same list. Removing the elements from the a list without changing the list itself mutates the list in place. As b references the same object, we see the same result there. If you did a = [] instead, then a will refer to a new empty list while b continues to reference the [1, 2, 3] list.
>>> list1 = [1,2,3,4,5]
>>> list2 = list1
To get a better understanding, let us see with the help of pictures what happens internally.
>>> list1 = [1,2,3,4,5]
This creates a list object and assigns it to list1.
>>> list2 = list1
The list object which list1 was referring to is also assigned to list2.
Now, lets look at the methods to empty an list and what actually happens internally.
METHOD-1: Set to empty list [] :
>>> list1 = []
>>> list2
[1,2,3,4,5]
This does not delete the elements of the list but deletes the reference to the list. So, list1 now points to an empty list but all other references will have access to that old list1.
This method just creates a new list object and assigns it to list1. Any other references will remain.
METHOD-2: Delete using slice operator[:] :
>>> del list1[:]
>>> list2
[]
When we use the slice operator to delete all the elements of the list, then all the places where it is referenced, it becomes an empty list. So list2 also becomes an empty list.
Well, del uses just a little less space in the computer as the person above me implied. The computer still accepts the variable as the same code, except with a different value. However, when you variable is assigned something else, the computer assigns a completely different code ID to it in order to account for the change in memory required.
There is this code:
a = [1, 2, 3, 4, 5]
a[:] = [] # and now a is also empty
Statement a[:] creates a copy of list a as I read, so if empty list [] is assigned to the copy then why original object is also modified?
x = a[:] makes x a new list containing the same values as a
a[:] = x makes the existing list a contain the same values as x
The behaviour of an expression changes when it switches sides of the equals.
Statement a[:] creates a copy of list a as I read
Except you're not reading, you're writing. Slice-assignment works differently in that it replaces the slice with the sequence.
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()