When I run the code below:
a=[0,1,2,3]
t=a
t.remove(3)
print(a)
it gives me a result of [0,1,2] even though I didn't use the remove() method for list a. Why does it happen?
Because list(and dicts) are pass by reference in Python. You have to shallow copy the list if you don't want it to happen like this:
t=a[:]
or
t=a.copy()
Lists in python are mutable objects and hence when you assigned t = a,
you created list 't' which is reference to list 'a'. Both lists refer to the same memory location.
Original list
a=[0,1,2,3]
id(a) # gives unique number which points to memory location
>> 60528136
assigning to t
t = a
id(t)
>> 60528136
The result shows same number which signifies that both lists refer to the same memory location. So any modification on list 'a' would be reflected in list 't' and vice-versa.
To avoid this create a copy.
t = a[::]
or
t = a.copy()
id(t)
>>60571720
Check out the Python docs on Programming FAQ here.
Why did changing list ‘y’ also change list ‘x’?
There are two factors that produce this result:
Variables are simply names that refer to objects. Doing y = x doesn’t create a copy of the list – it creates a new variable y that refers to the same object x refers to. This means that there is only one object (the list), and both x and y refer to it.
Lists are mutable, which means that you can change their content.
How do I copy an object in Python?
In general, try copy.copy() or copy.deepcopy() for the general case. Not all objects can be copied, but most can.
Some objects can be copied more easily. Dictionaries have a copy() method:
newdict = olddict.copy()
Sequences can be copied by slicing:
new_l = l[:]
Related
I was trying to figure out how memory allocation works with regards to a tuple in python.
skills = ("Python", "pandas", "scikit-learn")
print(id(skills))
This gives me a value of 140555133913704
I modify this tuple to create a new one
skills += ("ml","dl")
print(id(skills))
The new address is 140555133738680
Is the tuple object at 140555133913704 deleted and the address cleared?
Is modifying a tuple less or more space efficient as compared to modifying a list?
everything besides primitives in python is an object so when you type:
skills = ("Python", "pandas", "scikit-learn")
you are creating a tuple object and the reference to that is going to be stored in the skills variable you declared.
in the second step, you make another tuple object with the value of the last tuple in addition to "ml" and "dl" now you have the new object's reference in your skills variable.
what happened to the last one? well, it is still there but now it is an unreferenced object and the python garbage collector will clear that.
about lists, a list in python is also an object and is stored with the data of it and a "len" attribute which indicates the length of the array so if you append something to a list this will happen again although lists are mutable objects! (I actually got suspicious about this and tried it myself and it changed:) ) so again, the garbage collector will remove the unreferenced object after a while.
>>> list = [1,2,3]
>>> id(list)
2214374343624
>>> list = list + [2,3]
>>> list
[1, 2, 3, 2, 3]
>>> id(list)
2214472730440
It is because tuples are immutable and they cannot be changed. In your example adding tuple to a tuple will create a new tuple and assigns it to same name. It will not change the tuple object originally referenced by that name.
The tuple object at 140555133913704 will not be deleted. It will have previous value i-e, ("Python", "pandas", "scikit-learn")
Tuples are more space-efficient than lists. Lists are over-allocated to make appending faster.
I am quite confused about what mutable object really means
A=[1,3,4]
print(id(A))
A.append(1)
print(id(A))
The print-out shows the same address, while for the following
A=[1,3,4]
print(id(A))
A=A+[1,2]
print(id(A))
The first thing is that it doesn't report wrong since I expect since it is mutable it will do the iterative procedure, on the other hand, the address is different.
When you do
A = [1,2,3]
A.append(1)
you modify the list in place, thus causing no change in its address. On the other hand, when you do
A = [1,2,3]
A = A + [1,2]
you create a temporary new object A + [1,2] and then re-bind the existing list A to this newly created object (i.e. the newly created list), thus changing its address/id.
The first example mutates the list by calling append(). So the id() of the object is the same each time.
The second example reassigns the variable A to a new list that is the result of concatenating two lists. This does not mutate the original list. To see why, let's make a small modification:
A=[1,3,4]
print(id(A))
B=A+[1,2]
print(id(A))
print(id(B))
Now you will see that the id(A) is the same both times. Your original code is equivalent to this other than you no longer have a reference to the first list.
A = [1,2,3]
A.append(4) #just appends new element to list (but does not create new memory address to A)
A = A + [5,6] # first do [1,2,3,4] + [5,6] => [1,2,3,4,5,6], then assign this to A (which is creating and assigning new memory address to A).
Mutable objects means a data type that CAN CHANGE for example lists, dictionary, etcetera as you can always append or add values to them.
On the contrary, IMMUTABLE objects are those that CANNOT CHANGE, for example a TUPLE in python.
Hope it helped.
You must use .append() function
So I am running into something I cant explain and was hoping someone could shed light on... Here is my code:
fd = open(inFile, 'r')
contents = fd1.readlines()
fd.close()
contentsOrig = contents
contents[3] = re.sub(replaceRegex, thingToReplaceWith, contentsOrig[3])
Now when I print out contents and contentsOrig they are exactly the same. I was trying to preserve what I originally read in but from this little code it doesn't seem to be working. Can anyone enlighten me?
I am running Python 2.7.7
Yes, when you assign a list to another variable, it's not a copy of that list, it is a reference. Meaning there is still one copy of that list and now both variables point to it.
contentsOrig = contents
contentsOrig is contents
# Result: True
When you change one of the values or modify the list in any it is changing the same list. So what you need to do is make a copy of the list. This is done by either of these ways:
contentsOrig = contents[:]
or
contentsOrig = list(contents)
The first way is using the list slicing to produce a new list from the beginning to the end. The second takes a list and returns a new copy of the list.
Note that both ways do not make new copies of the items inside the list. So it's the same items, but different containers. But since these are strings, they are not mutable and therefore if modified inside a list, the original string will be untouched in the the original list.
In Python, mutable objects cannot be copied the way you are copying them. If effect, doing x = y only sets x and y to the same address in memory - they reference the same object (in this case a list). If you use the ID function id(x) and id(y) they will actually be the same!
Edit: note that the poster below showed x is y; the is keyword is using the id function in the background.
A simpler example is here:
list_one = [1, 2, 3]
list_two = list_one
list_one.append(4)
print list_one #will show [1, 2, 3, 4], as expected
print list_two #will ALSO show [1, 2, 3, 4]!
In this case, to get around this problem you can use list splicing to make the new list:
new_list = original_list[:]
If you have multiple nested lists (or other mutable objects), then you can use the deepcopy module to make a copy, if you want all the nested lists to be copied as well.
I understand that a namedtuple in python is immutable and the values of its attributes cant be reassigned directly
N = namedtuple("N",['ind','set','v'])
def solve()
items=[]
R = set(range(0,8))
for i in range(0,8):
items.append(N(i,R,8))
items[0].set.remove(1)
items[0].v+=1
Here last like where I am assigning a new value to attribute 'v' will not work. But removing the element '1' from the set attribute of items[0] works.
Why is that and will this be true if set attribute were of List type
Immutability does not get conferred on mutable objects inside the tuple. All immutability means is you can't change which particular objects are stored - ie, you can't reassign items[0].set. This restriction is the same regardless of the type of that variable - if it was a list, doing items[0].list = items[0].list + [1,2,3] would fail (can't reassign it to a new object), but doing items[0].list.extend([1,2,3]) would work.
Think about it this way: if you change your code to:
new_item = N(i,R,8)
then new_item.set is now an alias for R (Python doesn't copy objects when you reassign them). If tuples conferred immutability to mutable members, what would you expect R.remove(1) to do? Since it is the same set as new_item.set, any changes you make to one will be visible in the other. If the set had become immutable because it has become a member of a tuple, R.remove(1) would suddenly fail. All method calls in Python work or fail depending on the object only, not on the variable - R.remove(1) and new_item.set.remove(1) have to behave the same way.
This also means that:
R = set(range(0,8))
for i in range(0,8):
items.append(N(i,R,8))
probably has a subtle bug. R never gets reassigned here, and so every namedtuple in items gets the same set. You can confirm this by noticing that items[0].set is items[1].set is True. So, anytime you mutate any of them - or R - the modification would show up everywhere (they're all just different names for the same object).
This is a problem that usually comes up when you do something like
a = [[]] * 3
a[0].append(2)
and a will now be [[2], [2], [2]]. There are two ways around this general problem:
First, be very careful to create a new mutable object when you assign it, unless you do deliberately want an alias. In the nested lists example, the usual solution is to do a = [[] for _ in range(3)]. For your sets in tuples, move the line R = ... to inside the loop, so it gets reassigned to a new set for each namedtuple.
The second way around this is to use immutable types. Make R a frozenset, and the ability to add and remove elements goes away.
You mutate the set, not the tuple. And sets are mutable.
>>> s = set()
>>> t = (s,)
>>> l = [s]
>>> d = {42: s}
>>> t
(set([]),)
>>> l
[set([])]
>>> d
{42: set([])}
>>> s.add('foo')
>>> t
(set(['foo']),)
>>> l
[set(['foo'])]
>>> d
{42: set(['foo'])}
I have a function where I work with a local variable, and then pass back the final variable after the function is complete. I want to keep a record of what this variable was before the function however the global variable is updated along with the local variable. Here is an abbreviated version of my code (its quite long)
def Turn(P,Llocal,T,oflag):
#The function here changes P, Llocal and T then passes those values back
return(P, Llocal, T, oflag)
#Later I call the function
#P and L are defined here, then I copy them to other variables to save
#the initial values
P=Pinitial
L=Linitial
P,L,T,oflag = Turn(P,L,T,oflag)
My problem is that L and Linitial are both updated exactly when Llocal is updated, but I want Linitial to not change. P doesn't change so I'm confused about what is happening here. Help? Thanks!
The whole code for brave people is here: https://docs.google.com/document/d/1e6VJnZgVqlYGgYb6X0cCIF-7-npShM7RXL9nXd_pT-o/edit
The problem is that P and L are names that are bound to objects, not values themselves. When you pass them as parameters to a function, you're actually passing a copy of the binding to P and L. That means that, if P and L are mutable objects, any changes made to them will be visible outside of the function call.
You can use the copy module to save a copy of the value of a name.
Lists are mutable. If you pass a list to a function and that function modifies the list, then you will be able to see the modifications from any other names bound to the same list.
To fix the problem try changing this line:
L = Linitial
to this:
L = Linitial[:]
This slice makes a shallow copy of the list. If you add or remove items from the list stored in L it will not change the list Lintial.
If you want to make a deep copy, use copy.deepcopy.
The same thing does not happen with P because it is an integer. Integers are immutable.
In Python, a variable is just a reference to an object or value in the memory. For example, when you have a list x:
x = [1, 2, 3]
So, when you assign x to another variable, let's call it y, you are just creating a new reference (y) to the object referenced by x (the [1, 2, 3] list).
y = x
When you update x, you are actually updating the object pointed by x, i.e. the list [1, 2, 3]. As y references the same value, it appears to be updated too.
Keep in mind, variables are just references to objects.
If you really want to copy a list, you shoud do:
new_list = old_list[:]
Here's a nice explanation: http://henry.precheur.org/python/copy_list