Concepts / Mutable vs. Immutable Data Types

Mutable vs. Immutable Data Types

Passing a list to a function gives the function a reference to the original list, so modifications inside the function affect the caller's list.

  • Programming
Interactive lab

Try it: Names and Objects

How Python variables are names bound to objects: assignment never copies, mutating a list is seen through every name that points at it, and ints are replaced rather than changed.

How it works

  1. name = object binds a name; b = a makes b point at the same object.
  2. Mutating a list (append, +=) changes the one shared object.
  3. a = a + [x] and y = y + 1 create NEW objects and re-point one name.
  4. Passing a list to a function passes the reference, so the function can change it.

Default run (7 steps): Two names, one list. Every name is a label that points at an object. … Printed: [1, 2, 3] / True

Simplified: Six fixed scripts on a tiny heap; you choose the values. Object numbers are illustrative, not real id() values.

Educational simulation

Loading the simulation…

One List, Two Names

A function parameter can refer to the same list that the caller already has. The function does not receive a copy automatically. Therefore, if the function modifies that list in place, the caller sees the modification after the function finishes.

refers torefers toitems[1, 2, 3]List object[1, 2, 3]valuessame list object
When a caller passes a list, what object does the function parameter refer to?

Passing a list to a function gives the function a reference to the original list, not a copy.

Tracing an In-Place Change

What do you think happens?

After this function call, what will the caller's list contain?

  • [10, 20]
  • [10, 20, 30]
  • A separate list containing [30]
Reveal answer

Answer: [10, 20, 30]

append modifies the existing list object. The function parameter refers to the caller's original list, so the caller sees the added element.

def add_item(values): values.append(30) items = [10, 20] add_item(items) print(items)

same listsame listitems[10, 20]items[10, 20, 30]values[10, 20]values[10, 20, 30]
What changes inside the original list when the function uses append?

The important event is not merely that the function receives a parameter. The important event is what the function does with the referenced list. An operation such as append changes the existing object, so the change is visible through the caller's variable as well.

Mutation Versus New Lists

OperationEffect on the existing listResulting behavior
appendModifies the existing listThe caller sees the change when the list was passed to a function
delModifies the existing listThe caller sees the deletion when the list was passed to a function
+Does not modify the existing listCreates a new list
SlicingDoes not modify the existing listCreates a new list
appendexisting list changes+new listdelexisting list changesSlicingnew list
Which operations change the existing list, and which produce a separate list?
python
Output (expected)
[10, 20, 30]
[10, 20, 30, 30]
[10, 20]

In this example, the original items list remains [10, 20, 30]. The plus operation produces a new list, and slicing produces another new list. Because neither operation modifies items in place, the caller's original list is preserved.

Aliasing and Shared Changes

python
Output (expected)
[1, 2, 3]
[1, 2, 3]
refers torefers toitems[1, 2, 3]List object[1, 2, 3]other_name[1, 2, 3]
What happens when two variables refer to the same list and one is used for an in-place modification?

The same reference behavior can occur without a function. If two variables refer to one list, an in-place operation through either variable changes the shared list. Both variables then show the changed contents.

Two Function Design Choices

passmodify in placepassreturn new listOriginal listpassed to functionModify functionchanges inputChanged listcaller sees changeOriginal listpreservedNew-list functioncreates resultNew listreturned to caller
How does data flow differently when a function changes its input compared with when it creates and returns a new list?
python
Output (expected)
[1, 2, 3, 4]
[1, 2, 3]
[1, 2, 3, 4]

Use an in-place design when the caller expects the original list to change. Use a return-new-list design when preserving the original matters. Returning a new list is often safer because it avoids surprising side effects and lets the caller decide whether to keep the new result.

Mistakes with List References

  • Assuming that passing a list creates a copy.

    Passing the list gives the function a reference to the original list, not a copy. append modifies that existing list.

    Fix: Use an operation such as + or slicing to create a new list when the original must be preserved.

  • Treating append and + as equivalent.

    append modifies the existing list, while + creates a new list.

    Fix: Track whether the operation changes the existing object or produces a separate result.

  • Ignoring the function's design intention.

    In-place modification creates a side effect visible to the caller.

    Fix: Choose a function that returns a new list, or design the function to create and return one.

Practice the Trace

MEDIUM

For each function, predict the contents of the list named numbers after the call. Then decide whether the function modifies the original list or returns a new one. Function A: def change(numbers): numbers.append(8) Function B: def change(numbers): return numbers + [8] Use numbers = [2, 4] for both functions.

Hints
  • Ask whether the operation is append or +.
  • Remember that a function parameter refers to the list passed by the caller.
  • For the second function, identify which variable receives the returned list.

Tracing Two Different Designs

Start with numbers = [2, 4]. Compare a function that calls append with a function that returns numbers + [8].

Function A operation: append modifies the existing list in place.

Function A result: After calling change(numbers), numbers contains [2, 4, 8].

Function B operation: The + operation creates a new list rather than modifying the existing list.

Function B result: The original numbers remains [2, 4]. The new list is [2, 4, 8] and must be captured from the function's return value.

Function A changes the caller's list. Function B preserves the caller's list and provides a new list as its return value.

The Decision to Remember

  1. Passing a list to a function gives the function a reference to the original list, not a copy.
  2. In-place operations such as append and del modify the existing list, so the caller sees the change.
  3. The + operator and slicing create new lists, so the original list remains unchanged.
  4. Modify a list in place when the caller expects the original to change.
  5. Return a new list when preserving the original avoids surprising side effects and gives the caller control.

Key Takeaways

  • A function parameter referring to a list refers to the caller's original list.
  • In-place operations change that shared list and make the change visible to the caller.
  • The + operator and slicing create new lists instead of changing the original.
  • Function design should make the choice between mutation and preservation intentional.
  • Returning a new list is often safer when the original list should remain available to the caller.