Python

Understanding Python’s Pass-by-Object-Reference Mechanism

Understanding how Python handles variables and function arguments is essential for writing predictable and bug-free code. Many developers coming from languages like C++ or Java often wonder whether Python uses call by value or call by reference. The answer is: neither exactly. Python uses a model often called pass by object reference or call by sharing. Let us delve into understanding how passing by object reference works in Python.

1. Understanding Argument Passing: Value vs Reference

Before diving into Python’s behavior, it is important to understand the two traditional models of passing arguments to functions: call by value and call by reference. These concepts originate from languages like C, C++, and Java, and they help establish the foundation for understanding how Python behaves differently.

1.1 Call by Value: Working with Copies

In the call by value model, a function receives a copy of the variable’s value rather than the original variable itself. This means that any changes made inside the function are applied only to the copied value and do not affect the original variable outside the function. This approach ensures data safety, as the original variable remains unchanged regardless of what happens inside the function.

def modify(x):
    x = x + 10
    print("Inside function:", x)

a = 5
modify(a)
print("Outside function:", a)  # Output: 5

The variable a is passed to the function modify(), where a separate copy of its value (5) is created and assigned to x. The operation x = x + 10 modifies only this local copy, and as a result, the original variable a remains unchanged after the function call. This is why the output shows that the value of a is still 5 outside the function.

1.2 Call by Reference: Working with Original Data

In the call by reference model, instead of passing a copy, the function receives a reference (or address) to the original variable. This allows the function to directly modify the original data stored in memory. Any changes made inside the function are reflected outside the function because both the caller and the function refer to the same memory location.

# Conceptual example of call by reference (Python does not use &):
def modify(x):
    x.append(10)  # works for mutable objects like lists

The parameter x acts as a reference to the original variable, meaning no separate copy is created and both names point to the same memory location. As a result, updating x directly modifies the original variable. This is why, in languages that support call by reference, the original value is modified after the function call.

1.3 How Python Actually Passes Arguments

Python does not strictly use call by value or call by reference. Instead, it employs a model often called pass by object reference (or call by sharing). This means that when you pass a variable to a function, Python passes a reference to the object, not the actual value itself, but how the object behaves depends on its mutability.

  • The function receives a reference to the object, meaning it points to the same memory location as the original variable.
  • Whether changes inside the function affect the original variable depends on whether the object is mutable or immutable.

1.3.1 Understanding Mutability and Its Effect

  • Immutable objects (like int, float, str, tuple): cannot be changed in place. Any operation that seems to modify them actually creates a new object, leaving the original unchanged.
  • Mutable objects (like list, dict, set): can be modified in place. Changes inside the function affect the original object outside the function.

Understanding this distinction is crucial. It explains why immutable types behave like “call by value” (the original is unaffected) while mutable types behave like “call by reference” (the original can change). Once you internalize that Python always passes object references, the behavior of functions becomes predictable and intuitive.

2. Python in Action: Passing Objects to Functions

The following example demonstrates how Python handles immutable and mutable objects when passed to a function.

def modify_immutable(num):
    """
    Demonstrates call by value-like behavior using an immutable object (int).
    Changes inside the function do NOT affect the original variable.
    """
    print("Inside modify_immutable (before):", num)
    num = num + 10  # Creates a new integer object
    print("Inside modify_immutable (after):", num)


def modify_mutable(items):
    """
    Demonstrates call by reference-like behavior using a mutable object (list).
    Changes inside the function affect the original object.
    """
    print("Inside modify_mutable (before):", items)
    items.append(4)  # Modifies the same list object
    print("Inside modify_mutable (after):", items)


def main():
    # Immutable object
    number = 5
    print("Before calling modify_immutable:")
    print("number:", number)
    modify_immutable(number)
    print("After calling modify_immutable:")
    print("number:", number)  # Remains unchanged

    print("\n-----------------------------\n")

    # Mutable object
    my_list = [1, 2, 3]
    print("Before calling modify_mutable:")
    print("my_list:", my_list)
    modify_mutable(my_list)
    print("After calling modify_mutable:")
    print("my_list:", my_list)  # Gets updated


# Entry point
if __name__ == "__main__":
    main()

2.1 Code Explanation

This program demonstrates Python’s pass-by-object-reference behavior using both immutable and mutable objects. The function modify_immutable() takes an integer num, prints its initial value, adds 10, and prints it again; since integers are immutable, this operation creates a new object and does not change the original number variable in main(). In contrast, modify_mutable() takes a list items, prints its initial value, appends 4 to the list, and prints it again; because lists are mutable, this modifies the same object in memory, so changes are reflected in my_list outside the function. The main() function calls both functions and prints the state of the variables before and after the calls, clearly illustrating the difference between how Python handles immutable versus mutable objects when passed to functions.

2.2 Code Output

Run the following program using any Python interpreter (e.g., python filename.py) to observe how mutable and immutable objects behave when passed to a function.

Before calling modify_immutable:
number: 5
Inside modify_immutable (before): 5
Inside modify_immutable (after): 15
After calling modify_immutable:
number: 5

-----------------------------

Before calling modify_mutable:
my_list: [1, 2, 3]
Inside modify_mutable (before): [1, 2, 3]
Inside modify_mutable (after): [1, 2, 3, 4]
After calling modify_mutable:
my_list: [1, 2, 3, 4]

The output demonstrates how Python handles immutable and mutable objects differently when passed to functions. For the immutable integer number, the function modify_immutable() creates a new object when adding 10, so the original value remains 5 before and after the function call, showing call by value-like behavior. For the mutable list my_list, the function modify_mutable() appends 4 directly to the existing list object, so the change is reflected outside the function, resulting in [1, 2, 3, 4], demonstrating call by reference-like behavior. This output clearly illustrates that Python passes object references to functions, and the effect depends on whether the object is mutable or immutable.

3. Conclusion

Python does not strictly follow call by value or call by reference; instead, it uses pass by object reference, where function arguments are references to objects and the behavior depends on the object’s mutability. Immutable objects tend to behave like call by value because changes create new objects, while mutable objects behave like call by reference since they can be modified in place. Understanding this model helps avoid common pitfalls, especially when working with lists, dictionaries, and other mutable data structures, making Python’s behavior more predictable and intuitive once the concept is clear.

Yatin Batra

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button