Python Pass by Reference and by Value
Python pass by reference and pass by value behavior is probably one of the confusing topics for new (even for experienced) programmers that sometimes being overlooked.
For example, if you came from a PHP background, the default behavior of a parameter in a function is pass by value, and to make it pass by reference, you need to put an ampersand in front of the parameter.
<?php function foo(&$pets){ //modify $pets }
Pass by Reference and by Value
In Pass By Value you are passing the object’s value and anything you do with the parameter does not affect the Object.
When you Pass By Reference on the other hand, you are actually passing the ID of your object. The ID is the address of the object in your computer’s memory, hence anything you do with the parameter will affect the original value in the Object.
Mutable Object and Variable Assignment
An example of Python’s mutable objects are list, dictionary (dict) and set. For example below, variable “a” is a dictionary, and we are assigning it to variable “b”.
a = {'dog': 'pug'} b = a b['dog'] = 'chihuahua'
>>> print(a) {'dog': 'chihuahua'}
Our variable “a” did changed to chihuahua! That is because both variables “a” and “b” have the same ID when we passed by reference and “a” is a mutable object type dictionary.
>>> type(a) <type 'dict'> >>> type(b) <type 'dict'> >>> id(a) == id(b) True
Now that we know how variable assignment works in Python and how Pass by Reference affects mutable objects, let’s take a look at how we can apply parameters to a function.
To use default parameter value you must assign it during function definition. If you have required parameters, it has to be defined first followed by your optional parameters, otherwise you will get a syntax error.
SyntaxError: non-default argument follows default argument
Defining function parameters in proper order.
def foo(fruit, fav=[]): if fruit not in fav: fav.append(fruit) print 'fruit is not in fav' else: print 'fruit is in fav' return fav a = 'apple' b = ['banana','apple','orange']
>>> foo(a, b) # we expect fruit is in fav fruit is in fav >>> foo(a) # we did not pass "b" our fav will default to an empty list fruit is not in fav >>> foo(a) # we did not pass "b" but we got a different result fruit is in fav
On the first and second call to function “foo” we got the expected result. However, on the third call it was different! That is because the default value of parameter “fav” is a list, which is a mutable object, and the function keeps using the same object in each call. Therefore, if we modify the value of “fav” the object will retain the value on the next instance of “foo”. Let’s verify the ID of the returned parameter “fav” below.
>>> id(foo('apple')) #first call fruit is in fav 140092634617688 >>> id(foo('apple')) fruit is in fav 140092634617688 #second call has the same ID
In a real program this could cause a logical error because we did not get the intended result. To workaround this behavior, we can use a placeholder in our optional parameter instead of modifying the value inside the list object.
Parameter Placeholder
Now we will implement the placeholder in our function “foo”. Here we used “None” as our placeholder value (You can also use an empty “object()”).
def foo(fruit, fav=None): if fav is None: fav = [] if fruit not in fav: fav.append(fruit) print 'fruit is not in fav' else: print 'fruit is in fav' return fav a = 'apple' b = ['banana','apple','orange']
>>> foo(a, b) # we expect fruit is in fav fruit is in fav >>> foo(a) # we did not pass "b" our fav will default to an empty list fruit is not in fav >>> foo(a) # we did not pass "b" and we got the same result fruit is not in fav
This time we got the expected result in all calls to our function “foo”. Also notice that we checked if the object has changed in our parameter “fav” using the comparison “is None”.
Conclusion
In Pass by reference you are passing the identity of the object. Mutable objects can change value after it was declared, in contrast, there is also an Immutable object. Use a placeholder instead of modifying the value of a parameter inside your function to avoid unexpected result.