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
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.
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[:]
I was making a code, and variables started to behave strangely and get assigned to things which I thought they shouldn't. So, I decided to reduce the situation to minimal complexity in order to solve my doubts, and this is what happened:
The following code:
a = [2]
def changeA(c):
d = c
d[0] = 10
return True
changeA(a)
print(a)
prints '[10]'. This doesn't make sense to me, since I never assigned the list "a" to be anything after the first assignment. Inside the function changeA, the local variable d is assigned to be the input of the function, and it seems to me that this assignment is happening both ways, and even changing the "outside". If so, why? If not, why is this happening?
I've also noticed that the code
a = [2]
def changeA(c):
d = list(c)
d[0] = 10
return True
changeA(a)
print(a)
behaves normally (i.e., as I would expect).
EDIT: This question is being considered a duplicate of this one. I don't think this is true, since it is also relevant here that the locality character of procedures inside a function is being violated.
Python variables are references to objects, and some objects are mutable. Numbers are not, neither are strings nor tuples, but lists, sets and dicts are.
Let us look at the following Python code
a = [2] # ok a is a reference to a mutable list
b = a # b is a reference to the exact same list
b[0] = 12 # changes the value of first element of the unique list
print(a) # will display [12]
In the first example, you simply pass the reference of c(which is a) to d.
So whatever you do to d will happen on a.
In the second example, you copy the value of c(which is a) and give it to a new variable d.
So the d now has the same value as c(which is a) but different reference.
Note: you can see the reference or id of a variable using the id() function.
a = [2]
print id(a)
def changeA(c):
d = c
pirnt id(d)
d[0] = 10
return True
changeA(a)
print(a)
a = [2]
print id(a)
def changeA(c):
d = list(c)
print id(d)
d[0] = 10
return True
changeA(a)
print(a)
Its because when you do:
d = list(c)
that creates a new object. But when you do
d = c
You are making a reference to that object.
if you did
d.append(5)
to the first example you would get
[10,5]
Same operation to the second one and the list isn't modified.
Deeper explanation in the following link: http://henry.precheur.org/python/copy_list
In Python, names refer to values, so you have 2 names pointing to the same value.
In version 1 you create a list a, pass it in to the function under the pseudonym c, create another pseudonym d for the very same list, and then change the first value in that list. a, c, and d all refer to the same list.
In version 2, you're using list(c) which, to Python, means "take the contents of this iterable thing named c and make a new, different, list from it named d". Now, there are two copies of your list floating around. One is referred to as a or c, and the other is d. Therefore, when you update d[0] you're operating a second copy of the list. a remains unchanged.
'd=c' means reference copy as stated before. What it means, is that now d will reference the same object as c. As you are doing a direct manipulation on the referenced object, the value of the object a was referencing to is changed as well.
When you do 'd = list(c)' what it means that a new list object is created, with the baked of c. However, d is not referencing the same object as a anymore. Hence, the changes within the function doesn't impact a.
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'])}
Basically there seems to be a problem when a list of lists is made by appending a list to another list using list.append(). The problem is that the entire column is changed to the value you set it to for the index you give. for example the code here
b = [1,1,1]
c = []
c.append(b)
c.append(b)
c.append(b)
c[0][0] = 4
and if you print c you would get:
[[4,1,1][4,1,1][4,1,1]]
instead of
[[4,1,1][1,1,1][1,1,1]]
So my question is how would you be able to end up with the list to the right rather than what actually happens.
What you've observed is normal and expected behavior in Python and any decent tutorial should cover it.
c.append(b)
c.append(b)
c.append(b)
This appends to c three references to the list b. Since it's the same list, changing one reference changes it in all four places it's referenced, including b by the way.
If you don't want this behavior, copy the list.
c.append(b[:])
c.append(b[:])
c.append(b[:])
Make a new copy of the list each time you append it:
b = [1,1,1]
c = []
c.append(list(b))
c.append(list(b))
c.append(list(b))
c[0][0] = 4
The reason you're having problems is because lists are stored as a reference to the actual list object - and thus your version of c has 3 copies of the same reference, all pointing at the same actual [1,1,1] object. Using the list() function makes new copies so that the references point at separate objects.