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, not a copy.

  • 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.

items[1, 2, 3]List object[1, 2, 3]valuessame list objectrefers torefers to
When a caller passes a list, what object does the function parameter refer to?
Key point

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.

Code walkthrough
def add_item(values):
    values.append(30)

items = [10, 20]
add_item(items)
print(items)
  1. 1
    The caller creates a list containing 10 and 20.
  2. 2
    The caller passes that list to add_item.
  3. 3
    Inside the function, values.append(30) changes the existing list.
  4. 4
    After the function returns, printing items shows the changed contents.
  • The parameter values refers to the list passed by the caller.
  • append modifies that existing list in place.
  • The caller's variable items still refers to the modified list.
Output
[10, 20, 30]
items[10, 20]items[10, 20, 30]values[10, 20]values[10, 20, 30]same listsame list
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

The key distinction is whether an operation changes the existing list object or creates a separate 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]
items[1, 2, 3]List object[1, 2, 3]other_name[1, 2, 3]refers torefers to
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

Original listpassed to functionModify functionchanges inputChanged listcaller sees changeOriginal listpreservedNew-list functioncreates resultNew listreturned to callerpassmodify in placepassreturn new list
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

  • Common mistake

    Assuming that passing a list creates a copy.

    What it looks like

    A function receives values and uses values.append(5), but the caller expects its original list to remain unchanged.

    Why it fails

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

    Do this instead

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

  • Common mistake

    Treating append and + as equivalent.

    What it looks like

    Comparing values.append(5) with values + [5] as though both leave the original list in the same state.

    Why it fails

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

    Do this instead

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

  • Common mistake

    Ignoring the function's design intention.

    What it looks like

    Calling a function that modifies its list parameter when the caller needs to retain the original contents.

    Why it fails

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

    Do this instead

    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.
Worked example

Tracing Two Different Designs

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

  1. 1
    Function A operation

    append modifies the existing list in place.

  2. 2
    Function A result

    After calling change(numbers), numbers contains [2, 4, 8].

  3. 3
    Function B operation

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

  4. 4
    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.

Result

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. 1
    Passing a list to a function gives the function a reference to the original list, not a copy.
  2. 2
    In-place operations such as append and del modify the existing list, so the caller sees the change.
  3. 3
    The + operator and slicing create new lists, so the original list remains unchanged.
  4. 4
    Modify a list in place when the caller expects the original to change.
  5. 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.

Related concepts

More concepts starting with M