Function arguments – Pass by reference vs Pass by value

In Python, function arguments are pass by reference for mutable objects and pass by value for immutable objects. The concept of pass by reference and pass is value is similar in multiple programming languages. Understanding the function arguments concept and behavior of pass by reference vs. pass by value in Python is very crucial to avoid unexpected modifications to data and causing arbitrary results.

Pass by reference vs Pass by Value

Pass by reference means that a function receives the memory reference (or address) of the argument. It does not create a copy of the arguments value. Changes made to the argument inside the function will affect the original object outside the function as well.

Pass by value means the function receives a copy of the arguments value. It does not reference the actual object. Any changes made to the argument inside the function don’t affect the original object outside the function.

Python does not strictly follow pass by reference or pass by value. But it follows a pass by object reference model.

  • If we pass a mutable object (like lists, dictionaries, sets, user-defined classes), the function can modify the original object (because both objects reference the same memory location).
  • If we pass an immutable object (like integers, floats, strings, boolean or tuples), the function cannot modify the original object (because a new memory location is created parameter object and changes will not reflected in the original object).

Pass by reference example

# function arguments - pass by reference or pass by value
print("function arguments - pass by reference or pass by value")
def courses_reference(courses_list, course):
	print(len(courses_list))
	courses_list.append(course)   # modify mutable object - will affect original object

courses = ["AWS","Python","ML","DevOps"]    # list - mutable object
print("courses list before function call - ", courses)
courses_reference(courses, "Azure")  # list (mutable object) - pass by reference as function argument
print("courses list after function call - ", courses)  # courses elements after append
print("course added to - same reference of courses list")
  • We have defined a function with 2 parameters courses_reference(courses_list, course). It append course into the courses_list (mutable) object.
  • We have created a list (mutable) object => courses = ["AWS","Python","ML","DevOps"]
  • Calling the function with argument (mutable object) => courses_reference(courses, "Azure"). This argument object is mutable, it will be pass by reference. It means function, courses_list parameter will refer the memory location of the passed object. courses_list inside the function references the same list as courses outside the function.
  • When we append the course into the courses_list, it will affect the original argument list as well because lists are mutable and pass by reference.
  • courses list outside the function, will have the course added to that list.

Program Output

function arguments - pass by reference or pass by value
courses list before function call -  ['AWS', 'Python', 'ML', 'DevOps']  # original elements in list
4   # number of elements in passed argument
courses list after function call -  ['AWS', 'Python', 'ML', 'DevOps', 'Azure'] # elements after adding course to the list
course added to - same reference of courses list

Pass by value example

def modify_number(n):
    n += 10  # Creates a new integer, does not affect the original

number = 5
modify_number(number) # number (immutable) argument - pass by value
print(number)  # Output => 5
  • We have defined a function with 1 parameters modify_number(n). It add another number into the parameter value.
  • Calling the function with argument (immutable object) => modify_number(number). This argument object is immutable, it will be pass by value. It means function, n parameter will create a value of the passed object. n inside the function does not refer the same object as number which is outside the function.
  • When we add another number into the n, it does not affect the original number outside of function because numbers are immutable and pass by value.
  • The original number number = 5 remains unchanged.

Pass by reference with broken reference

# function arguments - pass by reference, with broken reference
print("function arguments - pass by reference, with broken reference")
def courses_broken_reference(courses_list, course):
	print(len(courses_list))   # length of original list
	# creating local variable with same name. This will break the reference to argument object
	courses_list = ["AWS","Python","ML","DevOps"]
	courses_list.append(course) # element added to the local list object, argument object remains unchanged

courses = ["AWS","Python","ML","DevOps"]    # list - mutable object
print("courses list before function call - ", courses)
courses_broken_reference(courses, "Azure") # list (mutable object) - pass by reference as function argument
print("courses list after function call - ", courses) # courses elements after append
print("course added was in - broken reference of courses list")
  • We have defined a function with 2 parameters courses_broken_reference(courses_list, course).
  • We have created a list (mutable) object => courses = ["AWS","Python","ML","DevOps"]
  • Calling the function with argument (mutable object) => courses_broken_reference(courses, "Azure"). This argument object is mutable, it will be pass by reference. It means function, courses_list parameter will refer the memory location of the passed object. courses_list inside the function references the same list as courses outside the function.
  • But we break the reference inside the function. When we created a local variable courses_list = ["AWS","Python","ML","DevOps"], which has the same name as parameter variable. Now operations inside the function will happen on this local variable and reference to passed argument is broken.
  • When we append the course into the courses_list, this happens on the local variable. Reference to argument list is broken and will not affect the original list outside the function.
  • courses list outside the function will not change.

Program Output

function arguments - pass by reference, with broken reference
courses list before function call -  ['AWS', 'Python', 'ML', 'DevOps'] # original elements in the list
4  # number of elements in the original list
courses list after function call -  ['AWS', 'Python', 'ML', 'DevOps']  # elements after broken reference
course added was in - broken reference of courses list

Pass by reference with immutable object

Immutable objects can be passed by reference, with wrapping into a mutable object. like tuple into a list or numbers into a list.

# pass by reference - with wrapping immutable object
print("pass by reference - with wrapping immutable object")
def modify_number(n):
    n[0] += 10  # Modifies the element inside the list

number_list = [5]  # Integer wrapped in a list
modify_number(number_list)
print(number_list[0])  # Output => 15
  • We have defined a function with 1 parameters modify_number(n). Here we are taking n as a list and adding another number at its first index value.
  • number_list = [5] defines the list of numbers. It wraps immutable number into a mutable list object.
  • Calling the function with argument (mutable object) => modify_number(number_list). This argument object is mutable, it will be pass by reference.
  • When we add another number into the first index of list n, it will affect the original number_list outside of function because lists are mutable and pass by reference.
  • The original number_list = [15] has updated value.

Function Annotations

Python allows adding metadata to function parameters and return values via annotations. Function annotations are optional and can be used for documentation or type hinting.

def add(a: int, b: int) -> int:
    return a + b

These annotations don’t enforce datatype for the variable. We can still call the function with different datatype argument. e.g. we can call add("shbytes", "courses") with string arguments. These annotations only serve as guidelines or hints for developers and IDEs.

Higher Order Functions

A function is called a higher-order function if it:

  • Takes one or more functions as arguments.
  • Returns a function as its result.

In Python, a function is allowed to take another function as an argument and can return a function as a result.

# Higher Order Functions
def apply_function(func, value):
    return func(value) # call the parameter function with argument

# call function with lambda function and value as arguments
print(apply_function(lambda x: x * 2, 10))  # Output => 20
  • We have created a function with 2 parameters => apply_function(func, value). This function takes another function as an argument and called this function argument value.
  • apply_function(lambda x: x * 2, 10) => This calls the function with lambda function and initial value as an argument. Inside apply_function, lambda function will be called with argument value 10. lambda function will calculate 10 * 2 and return its value.

Summary

Functions in Python are powerful tools for creating reusable, modular, and efficient code. By mastering function definitions, parameters, return values, scope, and advanced techniques like recursion and lambda expressions, we can write more robust and maintainable Python code. Following topics were discussed in this article.

Code – Github Repository

All code snippets and programs for this article and for Python tutorial, can be accessed from Github repository – Comments and Docstring in Python.

Python Topics


Interview Questions & Answers

Q: What is the difference between a function and a method in Python?

  • A function is a block of code that can be called by its name and is independent of any object.
  • A method is similar to a function but is associated with an object. Methods are defined inside classes and typically operate on data contained in the object (instance).
# This is a function
def shbytes():
    print("Python")

class Learning:
    # This is a method inside a class
    def shbytes(self):
        print("Python")

Q: What is function overloading in Python? Does Python support it?

Function overloading refers to defining multiple functions with the same name but different parameter types or counts.

Python being dynamically typed language, does not support traditional function overloading. However, we can achieve similar behavior by using default arguments or checking argument types inside the function.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *