I am learning Python using Lutz's book. I am using Python 3.6.5 from Anaconda distribution. I did research this problem on SO and didn't find any thread that answers my question. Mutability of lists in python talks about append
and not how we can pass a mutable object to a function.
My question is that when I make in-place changes to the list using index inside the function, the changes do get reflected, as expected because mutable objects are passed by reference. However, when I assign a list directly, the changes don't get reflected.
Specifically, I have created two lists L1
and L2
. For L1
, I would do assignment using index, but for L2
, I would do direct assignment inside the function.
L1=[2]
L2=['a']
print("Before, L1:",L1)
print("Before, L2:",L2)
def f(a,b):
a[0] =[3] #Using index-based assignment
b = ['b'] #Direct assignment
#Pass L to f
f(L1,L2)
print("After, L1:",L1)
print("After, L2:",L2)
The output is:
Before, L1: [2]
Before, L2: ['a']
After, L1: [[3]]
After, L2: ['a']
As we can see, L1
got changed, but not L2
.
Question: Can someone please explain why is it that the value of L2
doesn't get changed to 'b'
?
If you believe this post is duplicate, it will be great if you tag the relevant post.
As an aside, I ran a small experiment to see whether there is anything to do with index-based assignment or direct assignment.
l=[2]
id(l)
l[0] = 3 #Index assignment
id(l) # Memory location doesn't change
l = 3 # direct assignment
id(l) #Memory location changes.
Hence, it seems that there is a concept I am missing, meaning I am unsure why direct assignment changes the memory location.
If we change your code slightly, we can use id
to see how the references change (or don't change):
L1=[2]
L2=['a']
print("Before, L1:", L1, id(L1))
print("Before, L2:", L2, id(L2))
def f(a,b):
print("Inside, Before, a:", id(a))
print("Inside, Before, b:", id(b))
a[0] =[3] #Using index-based assignment
b = ['b'] #Direct assignment
print("Inside, After, a:", id(a))
print("Inside, After, b:", id(b))
#Pass L to f
f(L1,L2)
print("After, L1:", L1, id(L1))
print("After, L2:", L2, id(L2))
Output:
Before, L1: [2] 1870498294152 # L1 Before, L2: ['a'] 1870498294280 # L2 Inside, Before, a: 1870498294152 # L1 Inside, Before, b: 1870498294280 # L2 Inside, After, a: 1870498294152 # L1 Inside, After, b: 1870498239496 # Something different, not L2 After, L1: [[3]] 1870498294152 # L1 After, L2: ['a'] 1870498294280 # L2
Note, the numbers aren't significant in themselves other than to help distinguish references to different objects. Running this yourself (or if I ran it again), would cause the ids to change.
With a
, you're modifying/mutating a
but not attempting to re-assign the reference. That's fine.
With b
, you're re-assigning the reference. This will work inside the function (as the "Inside, After, b:" print call shows), but this change will not be reflected outside of the function. b
will be restored to reference the original object, ['a']
.
As to why...
meaning I am unsure why direct assignment changes the memory location.
Inside your function, a
and b
are just references to objects. Initially, they reference (the objects referenced by) L1
and L2
respectively because by calling f
, you're passing references to those objects.
a[0] = [3]
first dereferences a
(or L1
in this case), then the [0]
index, and sets that value.
In fact, if you look at id(a[0])
before and after that call then that would change. a
is a list of references. Try it:
print(id(a[0])) # One thing
a[0] =[3] #Using index-based assignment
print(id(a[0])) # Something different
This is fine. When you exit the function L1
will still reference the object that the function references using a
and it's mutation at the 0-index will remain.
With b = ['b']
you're reassigning or rebinding b
to a new object. The old object still exists (for later use outside the function).
Lastly, I use the term "reference" a lot, but Python is not precisely a "pass-by-reference" language, rather variable names are bound to objects. In the second, you're rebinding b
, losing the association to the originally-referenced object L2
forever.