Understanding Pass-by-Value vs. Pass-by-Reference in Python

One of the most commonly asked questions by Python developers is: “Does Python use pass-by-value or pass-by-reference?”

Does Python use pass-by-value or pass-by-reference?

The answer isn’t as straightforward as it is in languages like C++ or Java. Python follows a “pass-by-object-reference” model, which is sometimes referred to as “pass-by-assignment.”

This article will explore how Python handles function arguments and how the behavior differs for mutable and immutable objects.

What is Pass-by-Value and Pass-by-Reference?

Before diving into Python, let’s first understand these two common parameter-passing mechanisms:

  • Pass-by-Value: A copy of the variable is passed to the function. Modifications inside the function do not affect the original variable.
  • Pass-by-Reference: A reference (memory address) of the variable is passed to the function. Changes inside the function affect the original object.

Python does not strictly follow either of these. Instead, it passes references to objects. However, the behavior depends on whether the object is mutable or immutable.

Parameter Passing in Python

Immutable Objects (Pass-by-Value-like Behaviour)

Immutable objects in Python include:

  • Integers (int)
  • Floating-point numbers (float)
  • Strings (str)
  • Tuples (tuple)
  • Booleans (bool)

Example For Immutable

x =1
print(id(x)) #4356531368
x = 9
print(id(x)) #4356531624
Python

When you pass an immutable object to a function, Python passes a reference to the object. However, since these objects cannot be modified in place, any changes inside the function result in a new object being created rather than modifying the original.

But we change values of an integers,string etc, why we call them Immutable?

This is a common point of confusion for most of us. It looks like you’re changing them because internally Python lets you reassign variables.

Example

x = 5
x = x + 1
print(x)  # Output: 6
Python

This might look like you “changed” the integer 5, but what really happened is:

  • Python created a new integer object 6.
  • The variable x was updated to point to this new object.
  • The original 5 still exists in memory (and might be reused by other variables if needed), but x no longer refers to it.

Example

x = 5
print(id(5))
print(id(x))
x = x + 1
print(id(x))
y =5
print(id(y))

'''
4314554656
4314554656
4314554688
4314554656
''''
Python

  • Y reuses the variable

Example: Passing an Integer

def modify(x):
    print("Before modification:", x, "ID:", id(x))
    x = x + 10  # Creates a new integer object
    print("After modification:", x, "ID:", id(x))

num = 5
modify(num)
print("Outside function:", num, "ID:", id(num))
Python

Output

Before modification: 5 ID: 140704148661776
After modification: 15 ID: 140704148662096
Outside function: 5 ID: 140704148661776
Python

Key Takeaway:

  • The num(num =5) variable remains unchanged because integers are immutable.
  • So modifying them inside the function creates a new objects, leaving the original unchanged.

Mutable Objects (Pass-by-Reference-like Behaviour)

Mutable objects include:

  • Lists (list)
  • Dictionaries (dict)
  • Sets (set)
  • Bytearrays (bytearray)

If a function modifies a mutable object in place, the changes will reflect outside the function as well.

Example: Passing a List

def modify_list(lst):
    print("Before modification:", lst, "ID:", id(lst))
    lst.append(4)  # Modifies the same list object
    print("After modification:", lst, "ID:", id(lst))

numbers = [1, 2, 3]
modify_list(numbers)
print("Outside function:", numbers, "ID:", id(numbers))
Python

Output

Before modification: [1, 2, 3] ID: 140315953014976
After modification: [1, 2, 3, 4] ID: 140315953014976
Outside function: [1, 2, 3, 4] ID: 140315953014976
Python

Since lists are mutable, modifications inside the function affect the original list.

Reassigning a Mutable Object Inside a Function

If you reassign a mutable object inside a function, the reference inside the function now points to a new object. However, the original variable outside the function remains unchanged.

Example: Reassigning a List

def modify_list(lst):
    print("Before reassignment:", lst, "ID:", id(lst))
    lst = [10, 20, 30]  # New list assigned
    print("After reassignment:", lst, "ID:", id(lst))

numbers = [1, 2, 3]
modify_list(numbers)
print("Outside function:", numbers, "ID:", id(numbers))
Python

Output

Before reassignment: [1, 2, 3] ID: 140522834077056
After reassignment: [10, 20, 30] ID: 140522834121472
Outside function: [1, 2, 3] ID: 140522834077056
Python

Key Takeaway: Reassigning a new list inside the function does not change the original numbers list.

Avoiding Unwanted Changes: Using .copy()

To avoid modifying the original mutable object, you can pass a copy of the object.

def modify_list(lst):
    lst = lst.copy()  # Creates a new list
    lst.append(99)
    print("Inside function:", lst)

numbers = [1, 2, 3]
modify_list(numbers)
print("Outside function:", numbers)  # Original list remains unchanged
Python

Output

Inside function: [1, 2, 3, 99]
Outside function: [1, 2, 3]
Python

Since .copy() creates a new list, modifications inside the function do not affect the original numbers.

Practice

Guess the output


name = "John"
upper_names = ["ALICE", "BOB"]


def add_upper_name(name, upper_names):
    name = name.upper()
    upper_names.append(name)

add_upper_name(name, upper_names)

print(name, upper_names)

#John ['ALICE', 'BOB', 'JOHN']
Python

Conclusion

In Python, function arguments are passed by object reference, which means that the behavior depends on whether the object is mutable or immutable.

  • Immutable objects (int, str, tuple) behave like pass-by-value, since any modification creates a new object rather than altering the original.
  • Mutable objects (list, dict, set) behave like pass-by-reference, allowing in-place modifications that affect the original object.
  • Reassigning a mutable object inside a function does not change the original variable—it simply binds the local variable to a new object.

If you need to modify a mutable object without affecting the original, use .copy() or deepcopy() for nested structures.

Understanding Python’s parameter-passing model is crucial for writing efficient, bug-free code and avoiding unintended side effects. Next time you pass an object to a function, ask yourself: “Am I modifying it in place, or am I creating a new object?”

Leave a Comment