Pass by Assignment in Python 2026 – Understanding References and Mutability

Writing Functions Mar 16, 2026

Pass by Assignment in Python 2026 – Understanding References and Mutability

Python does not use "pass by value" or "pass by reference" like other languages. Instead, it uses pass by assignment. Understanding this concept is crucial for writing correct and efficient functions, especially when working with mutable objects.

TL;DR — Key Takeaways 2026

  • Python passes objects by assignment: the parameter name is bound to the same object
  • Immutable objects (int, str, tuple, etc.) appear to be passed by value
  • Mutable objects (list, dict, set, custom classes) can be modified inside functions
  • Use copy() or deepcopy() when you need to protect original data

1. Core Concept: Pass by Assignment


def modify_list(lst):
    lst.append(99)        # Modifies the original list

def reassign_list(lst):
    lst = [1, 2, 3]       # Only rebinds the local name

my_list = [10, 20, 30]
modify_list(my_list)
print(my_list)            # [10, 20, 30, 99] ← Changed!

reassign_list(my_list)
print(my_list)            # [10, 20, 30, 99] ← Not changed!

2. Immutable vs Mutable Behavior


# Immutable → behaves like pass by value
def increment(n):
    n += 1                # Creates a new integer object

x = 5
increment(x)
print(x)                  # Still 5

# Mutable → can be modified in place
def add_item(items):
    items.append("new")

my_list = ["a", "b"]
add_item(my_list)
print(my_list)            # ['a', 'b', 'new']

3. Best Practices in 2026

  • Be explicit about whether you intend to mutate or rebind
  • Use copy() or deepcopy() when you want to protect the original object
  • Prefer immutable data structures (tuples, frozensets, NamedTuple) when possible
  • Document mutation behavior clearly in function docstrings
  • Avoid side effects in pure functions when possible

What Pass by Assignment Means in Practice

When you call a function, Python binds each parameter name to the same object the caller passed. Nothing is copied. This model is called pass by assignment, or pass by object reference, because it works exactly like an assignment statement: param = argument.

Two consequences follow. If the function mutates the object, for example by appending to a list or setting a key in a dictionary, the caller sees the change because both names point to one object. If the function rebinds the parameter to a new object with =, only the local name changes and the caller's variable is untouched.

This explains the behaviour of immutable values too. Integers, strings and tuples cannot be changed in place, so any operation that seems to modify them actually creates a new object and rebinds the local name. The caller never sees a difference.

The id() function, which returns an object's identity, is a simple way to see whether two names refer to the same object.

Worked Example: Mutation Versus Rebinding

We pass the same list to two functions, one that mutates it and one that rebinds the parameter.

def add_item(items):
    print("inside, same object:", id(items) == id(basket))
    items.append("tea")        # mutation: caller sees it

def replace_items(items):
    items = ["coffee"]         # rebinding: only the local name changes
    return items

basket = ["milk"]
add_item(basket)
print(basket)

new = replace_items(basket)
print(basket, new)

Inside add_item the parameter is the same object as basket, and after the call basket is ['milk', 'tea']. replace_items returns ['coffee'] but leaves basket unchanged, because assigning to items only rebinds the local name.

Practical Tips

If a function must change an argument, say so in its name or docstring, for example update_totals_in_place. Otherwise return a new object and leave the input alone.

When you need a safe working copy, use list(items), dict(d) or copy.deepcopy for nested data, and do it at the top of the function so the intent is clear.

Common Mistakes

  • Expecting a function to change the caller's integer or string.
  • Mutating a list argument when the caller did not expect it.
  • Thinking that = inside a function changes the caller's variable.
  • Copying large objects defensively when a clear return value would do.

Quick Summary

  • Parameters are bound to the caller's objects.
  • Mutation is visible to the caller.
  • Rebinding with = is local.
  • Return new values instead of mutating when in doubt.

Conclusion

Understanding "pass by assignment" is fundamental to writing predictable and bug-free Python functions. In 2026, developers who master this concept write clearer code, avoid unexpected mutations, and make better decisions about when to copy data versus mutate in place.

Next steps:

  • Review your functions and make mutation behavior explicit

Related Tutorials

Share:
Last updated: March 2026
Built-in Functions in Python 2026 str() in Python 2026: String Conversion + Modern Formatting & Best Practices memoryview with JAX in Python 2026: Zero-Copy NumPy → JAX Array Interop + Efficient ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + GPU Pinning & ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + ML Examples memoryview with NumPy & PyTorch in Python 2026: Zero-Copy Views, Efficient Slicing & ML Interop Examples memoryview with NumPy in Python 2026: Zero-Copy Views, Efficient Slicing & Real ML Examples memoryview() in Python 2026: Zero-Copy Magic for Large Binary Data + Real Examples import() in Python 2026: How It Works, Security Risks & Modern Alternatives (importlib) zip() in Python 2026: How to Use It, strict=True Behavior & Real-World Examples vars() in Python 2026: Accessing Object Namespace + Modern Introspection Patterns type() in Python 2026: Dynamic Type Inspection & Object Creation + Modern Patterns tuple() in Python 2026: Immutable Sequences + Modern Patterns & Best Practices super() in Python 2026: Method Resolution & Modern Inheritance Patterns sum() in Python 2026: Summing Iterables + Modern Numeric Patterns & Best Practices staticmethod() in Python 2026: Static Methods, Utility Functions & Modern Best Practices sorted() in Python 2026: Sorting Iterables + Modern Patterns & Best Practices slice() in Python 2026: Creating Slice Objects + Modern Patterns & Best Practices setattr() in Python 2026: Dynamic Attribute Setting + Modern Patterns & Safety set() in Python 2026: Mutable Sets Creation + Modern Patterns & Best Practices