Concepts / Memory and References in Python

Memory and References in Python

Lists are passed to functions by reference, meaning the function receives access to the original list, not a copy.

  • Programming

One List, Two Names

Suppose a caller has a list named letters and passes it to a function. Inside the function, the parameter is another name for access to that same list object. The function does not receive a separate copy. This is why an operation inside the function can change what the caller sees after the function returns.

refers torefers toletterscaller name['a', 'b', 'c']one list objecttfunction parameter
What contains what, and how can the function parameter and the caller's variable point to the same list object in memory?

Tracing a Deletion

What do you think happens?

Before the function delete_head is called, letters contains ['a', 'b', 'c']. The function deletes the element at index 0 through its parameter t. What does letters contain afterward?

  • ['a', 'b', 'c']
  • ['b', 'c']
  • []
Reveal answer

Answer: ['b', 'c']

The parameter t and the variable letters refer to the same list object. Deleting the element at index 0 changes that shared object, so the caller sees ['b', 'c'] after the function returns.

The delete_head example

A list named letters contains ['a', 'b', 'c']. The function delete_head receives that list through parameter t and removes the element at index 0.

Before the call: letters refers to the list containing ['a', 'b', 'c'].

During the call: The parameter t refers to the same list object. The deletion through t removes 'a' from that object.

After the call: letters still refers to the same object, which now contains ['b', 'c'].

The deletion made through the function parameter is visible through the caller's variable.

refers torefers toletterscaller variable['a', 'b', 'c']list objectletterscaller variable['b', 'c']list object
What changes in the original list after the function removes the first element through its parameter?

Reference Access

A value and a reference are different ideas in this explanation. A number is treated as a value: when a number is passed to a function and modified inside the function, the original number outside remains unchanged. A list is an object. When a list is passed to a function, Python passes a reference to that object rather than a copy of the list itself.

The important question is not which name you are using. The important question is whether an operation modifies the shared list object. If it does, the caller can see the change because both names refer to that object.

passes referenceaccessesreceives mutationcallerlettersparameter tfunction accesslist object['a', 'b', 'c']delete index 0changes the object
How does the list reference move from the caller into the function, and where does the function's access lead?

Mutation and Rebinding

OperationWhat happens to the list objectEffect visible to caller
Modify through the parameterThe shared list object changesYes
Reassign the parameterThe parameter refers to a different listNo

Reassigning the parameter is different from modifying the list. If the function assigns the parameter to a new list such as [1, 2, 3], that changes what the parameter refers to, but it does not change the original list variable in the caller. Only modifications to the shared list object are visible to the caller.

modifieschange visible throughrefers todoes not refer totparametertparametershared listobject is modified[1, 2, 3]new listletterscaller sees changelettersoriginal list unchanged
What is the difference between changing the shared list object and assigning the parameter to a new list?

Unexpected Changes

Unexpected list mutations often become easier to find when you trace every function call that received the list. Any of those functions could have modified the shared object. Follow the list from the caller into each parameter, then identify the operation that changed the object.

passes list referenceaccesses through tdeletes index 0callerlettersdelete_headparameter tlist object['a', 'b', 'c']['b', 'c']after deletion
Which function call changed the shared list, and how can the sequence of references and mutations be traced?
  • Assuming the function receives a copy of the list.

    The function parameter and the caller's variable are aliases for the same list object.

    Fix: Check whether the function modifies the list object itself. Such a modification is visible to the caller.

  • Treating parameter reassignment as if it were list mutation.

    Reassigning the parameter changes the parameter's reference, not the caller's original list.

    Fix: Separate operations that modify the shared object from assignments that make the parameter refer to another list.

  • Debugging only the caller and ignoring functions that received the list.

    A function that receives the list can modify the original object through its parameter.

    Fix: Trace each call that passed the list and inspect the operations performed through the corresponding parameter.

Check Your Reasoning

MEDIUM

A caller has a list named data. A function named remove_negatives receives data and deletes elements through its parameter numbers. Predict whether data can change after the function returns, then explain why.

Hints
  • Ask whether numbers refers to the original list object or to a copy.
  • Focus on whether the function deletes elements from the list object.
  • Use the distinction between mutation and parameter reassignment.

The expected conclusion is that data can change. The function receives a reference to data's list object, and deleting elements through numbers modifies that actual object. When the function returns, the caller sees the modified list.

Remember the Path

  1. Passing a list to a function gives the function a reference to the original list rather than a copy.
  2. The caller's variable and the function parameter are aliases for the same list object.
  3. Mutating the list inside the function is visible to the caller.
  4. Reassigning the parameter to another list does not change the caller's original list.
  5. To debug an unexpected mutation, trace every function call that received the list and inspect how each function used its parameter.

Key Takeaways

  • A list argument gives a function access to the original list object.
  • The parameter inside the function and the caller's variable are aliases for that object.
  • Changes made to the shared list are visible outside the function.
  • Rebinding the parameter is not the same as modifying the shared list.
  • Tracing references and mutations through function calls helps explain unexpected list changes.