Did you ever find yourself struggling to find out why an assignment is not working as you expected or a variable is being modified magically?
You should have heard of call-by-value and call-by-reference. These are evaluation strategies that many popular programming languages use. An evaluation strategy is a set of rules that defines how expressions are evaluated in a programming language.
In Python, assignment always updates the reference and doesn’t change the previous referenced object.
var = 3
var = 5
First assignment will create a variable named “var” in the local context then create an int object that has a value of 3. Finally it updates the reference of var to point to the new int object. Doing one more assignment will create a new int object again and updates the reference of var to point to this object. So it won’t do anything directly to the previous object that has a value of 3. Additionally, if there isn’t any reference to the old object like in this example, it will be garbage collected.
class A():
pass
a = A()
a.var = 3
a.var = 5
Doing an assignment operation on an object variable will behave the same as previous example. Reference of “var” will change but reference of “a” won’t be affected by the assignment.
There is actually two cases that makes it behave like call-by-value or call-by-reference. I will try to explain these cases.
Call-by-value means the variable that you pass to a function will not change the original variable, instead use a copy. So, we need to create copies of variables.
To create a copy you can use the slice syntax if the object in question is iterable:
copy_of_var = var[:]
Or if it is a builtin:
copy_of_var1 = list(var1)
copy_of_var2 = dict(var2)
copy_of_var3 = set(var3)
Or as a last resort you can use the copy module.
You can create a copy of a variable before passing to a function or before returning a value from a function. Below are some examples.
Copy before passing:
def append_one(lst):
lst.append(1)
return lst
lst = [2, 3]
append_one(list(lst)) # here!
Copy before returning:
def append_one(lst):
lst = list(lst) # here!
lst.append(1)
return lst
lst = [2, 3]
append_one(lst)
I think copying inside the function is more intuitive than the other but I don’t see any side effect of using the other.
Call-by-reference means a function can modify the original variable that is passed from the context where the function call occurred. Changing items in an object does not change the object itself (object should mutable, of course). So if we wrap a variable inside a list, for example, it will be accessible both from the context of the function call and inside the function.
def add_one(n):
n[0] += 1
n = [3]
add_one(n)
print n[0] # prints 4
Also, some of the builtin functions work “in place”:
def sort_list(lst):
lst.sort()
lst = [7, 3, 9]
sort_list(lst)
print lst # prints [3, 7, 9]
My name is Ertuğ Karamatlı. I'm working at sahibinden.com and doing a PhD in Computer Engineering at Boğaziçi University. Contact me at ertug@karamatli.com.
home · rss · knowledge base · twitter · github