Python, as far as I'm concerned, is just like Java, in the fact that all functions (methods) are pass-by-value. See this infamous answer. The downfall of "call by sharing" is that it makes you think something different is happening for different types (i.e. mutable types behave differently in function calls than immutable types). This really is not the case, the same thing happens every time you call a function. Also, no textbook I've read mentions the concept, I think pass-by-value and pass-by-reference are much more popular terminology.
How does pass by value work?
It works by copying the "value" which was passed in to the function before modifying it in the function. So, take this example:
my_int = 4
def pass_by_value(value):
value += 1
return value
print pass_by_value(my_int)
print my_int
this outputs:
5
4
Observe that the value of my_int
didn't change. It stayed at 4
even though the function incremented the value which was passed in to be 5
. This is because it was copied. There are several subtleties here which we will return to. Bear with me.
Pass by value on a list
my_list = [1,2,3]
def pass_by_value_list(li):
li.append(4)
return li
print pass_by_value_list(my_list)
print my_list
this outputs:
[1, 2, 3, 4]
[1, 2, 3, 4]
What the heck. The value of my_list
changed. You JUST SAID ABOVE that pass-by-value copies the value! Therefore, my_list
should not have changed!!!
The first example was too subtle. I failed to properly define what "value" was being copied. It turns out, the value that is copied is not the data, but rather, where the data is stored. Typically C/C++ programmers call this a pointer or address. Let's revisit the examples, but modify them a bit to drive home the point.
How does pass by value work? Version 2.0:
my_int = 4
def pass_by_value(value):
print "Address of parameter before += is: ", id(value)
value += 1
print "Address of parameter after += is: ", id(value)
return value
print "Address of parameter outside of the function is: ", id(my_int)
pass_by_value(my_int)
When I ran this, my output was:
Address of parameter outside of the function is: 40592528
Address of parameter before += is: 40592528
Address of parameter after += is: 40592504
Looks like the address of the parameter before the +=
operator was 40592528. This is also the same "value" that the parameter outside the function had! But after the +=
operator, the "value" changed inside the function! However, that change in address was not propagated outside the function, because the address is pass-by-value. The new address inside the function is 40592504 (different from 40592528, albeit close). Long story short, the +=
operator makes a new int
, and does not operate on the data of the address passed into the function.
Pass by value on a list, Version 2.0:
my_list = [1,2,3]
def pass_by_value_list(li):
print "Address of parameter before .append() is: ", id(li)
li.append(4)
print "Address of parameter after .append() is: ", id(li)
return li
print "Address of parameter outside of the function is: ", id(my_list)
pass_by_value_list(my_list)
this output:
Address of parameter outside of the function is: 110841160
Address of parameter before .append() is: 110841160
Address of parameter after .append() is: 110841160
Hey, that's different than the integer case. Looks like all the addresses are the same! Indeed, this is different from the integer case. The append
function operates on a list in place, and does not return a new list. This is why you see a change to my_list
outside of the function. append
modifies the list at the address which was passed in to the function, so we see the data changed everywhere throughout the program. What didn't change, though, was the address.
Observe that:
my_list = [1,2,3]
def dont_change_the_list(li):
li.append(4)
li = []
return li
print dont_change_the_list(my_list)
print my_list
outputs
[]
[1, 2, 3, 4]
In other words, the change to the list inside the function wasn't propagated outside the function, even though this seemed to be the behavior we saw before. This is because the statement li = []
changes the address of li
, but we copied the address before the function executed. The address of the parameter is the "value" which is copied, and is not possible to change inside of a function. So, this did not change the data in my_list
in the main program.
The point: Python always uses pass-by-value semantics on its function calls. The value just happens to be the address of the object which was passed in, not the data of the object which was passed in.