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 Behavior)

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.

    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 Behavior)

    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.

    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