Concepts / Understanding List Mutability

Understanding List Mutability

Reassigning a function parameter (e.g., t = t[1:]) does not modify the original list passed to the function; it only changes what the local variable refers to.

  • Programming

Two Ways a List Can Change

When working with lists, two different events can look similar at first. A function can change the contents of the existing list object, or it can make a local variable refer to a different list. The first kind of change is visible through the caller's list. The second kind changes only the local variable's reference.

The central question is not simply whether a list expression appears inside a function. Ask whether the operation modifies the existing list object or creates a new list and reassigns a variable.

changes referencechanges contentsRebindingvariable refers to a newlistNew listoriginal list unchangedMutationexisting list contentschangeExisting listcaller observes the change
What is the difference between changing what a variable refers to and changing the contents of the list it already refers to?

Tracing a Slice Assignment

What do you think happens?

A function receives a list and performs t = t[1:]. What happens to the list in the caller?

  • The caller's list loses its first element
  • The local parameter refers to a new list, while the caller's list stays unchanged
  • Both the caller's list and the local parameter become empty
Reveal answer

Answer: The local parameter refers to a new list, while the caller's list stays unchanged

The slice creates a new list. Reassigning t changes what the local variable t refers to; it does not modify the original list object.

Suppose a caller provides a list to a function. Inside the function, the parameter initially refers to the list supplied by the caller. The expression t[1:] creates a new list containing the slice, and the assignment t = t[1:] makes the local parameter refer to that new list. The original list passed by the caller is left unchanged.

parameter initially refers tot[1:] createsassignment makes t refer toitems[10, 20, 30]t[1:][20, 30]items[10, 20, 30]tsame list as itemstrefers to the slice
What happens to the caller's list and the local parameter when a function executes t = t[1:]?

Removing the First Element Without Changing the Caller List

A function attempts to remove the first element by reassigning its parameter to a slice.

Initial state: The caller's list and the function parameter initially refer to the same list object.

Slice creation: The expression t[1:] creates a new list containing all elements except the first.

Local reassignment: The assignment t = t[1:] makes the local parameter refer to the new list.

Caller observation: The caller still has the original list because no operation modified that existing list object.

Reassigning the parameter to a slice does not modify the original list.

Slicing and New List Objects

Slicing is an operation that creates a new list. The new list contains the selected elements, but it is not the original list object. The original remains available through any variable that still refers to it. The plus operator also creates a new list, so using slicing or + does not by itself modify the original list.

slice createsOriginal list[10, 20, 30]Sliced list[20, 30]
Which list object does a slice refer to before and after the slice operation, and how are the original and new lists related?
Operation categoryWhat happensOriginal list
In-place methodThe existing list object is modifiedChanges are visible to the caller
SlicingA new list is createdRemains unchanged
Plus operatorA new list is createdRemains unchanged

Changing the Existing List

In-place methods operate on the actual list object. Methods such as append() and pop() modify that existing object. If a function parameter refers to the caller's list and the function uses one of these methods, the caller can observe the changed contents after the function finishes.

append modifiessame list objectitems[10, 20]items[10, 20, 30]trefers to itemststill refers to items
How does an in-place method change the existing list object while preserving the reference held by the caller?

Use an in-place method when the function's purpose is to modify the list supplied by its caller. Make that intent clear through the function's naming and documentation so users know that the original list will change.

Designing List Functions

A function that modifies a list and a function that returns a new list have different effects. For the modifying version, use an in-place operation such as append() or pop(). For the non-modifying version, use an operation such as slicing or + and return the resulting new list. The choice should match the function's purpose.

passes listcalls on listmodifies existing objectCaller list[10, 20]Function parameterrefers to caller listappend()in-place operationCaller list[10, 20, 30]
How does data move from the caller's list into a function, and which operation causes the caller to observe the modification afterward?
slicing createsseparate listOriginal list[10, 20, 30]Original list[10, 20, 30]Returned list[20, 30]
What lists exist before and after a function returns, and how can the returned list change while the original remains unchanged?

Choosing the Function's Effect

Decide whether a function should use an in-place operation or return a new list when its purpose is to remove the first element.

Modify in place: Choose an in-place list operation when the caller should see the original list changed.

Return a new list: Choose slicing when the original list should remain unchanged and the function should provide a separate result.

Document the intent: Make the choice clear through the function's naming and documentation.

The correct design depends on whether changing the caller's list is part of the function's purpose.

Common Mistakes

  • Assuming that t = t[1:] removes the first element from the caller's list.

    Slicing creates a new list, and the assignment changes only what the local parameter refers to.

    Fix: Use an in-place list method when the function must modify the original list.

  • Treating slicing and in-place methods as equivalent.

    Slicing leaves the original list unchanged.

    Fix: Use an operation such as append() or pop() for an in-place change.

  • Changing a list in place when the function should preserve the original.

    In-place methods modify the actual list object and make the change visible to the caller.

    Fix: Use slicing or the plus operator to create a new list and return it.

Practice the Distinction

MEDIUM

For each function design, decide whether it should use an in-place method or create and return a new list. Then explain what the caller's original list will contain after the function finishes.

Hints
  • Ask whether the function's purpose includes changing the caller's list.
  • Remember that append() and pop() modify the existing list object.
  • Remember that slicing and + create new lists.
  1. A parameter can refer to the caller's list without every operation on the parameter changing that list. In-place methods such as append() and pop() modify the existing list object, so the caller sees the change. Slicing and the plus operator create new lists, so assigning their result changes a variable's reference rather than the original list. Choose the operation that matches the function's intended behavior and make that intent clear through naming and documentation.

Key Takeaways

  • Reassigning a function parameter to a slice changes the local parameter's reference, not the original list.
  • In-place methods such as append() and pop() modify the existing list object.
  • Slicing and the plus operator create new lists and leave the original list unchanged.
  • Choose between mutation and returning a new list according to the function's purpose.
  • Use naming and documentation to make a function's list-changing behavior clear.