Concepts / Debugging Unintended Side Effects

Debugging Unintended Side Effects

Assignment creates a reference to a list, not a copy. Both variables point to the same object, so modifications through one affect the other.

  • Programming

When One Change Appears Twice

A list can change in a place you did not expect. One common cause is assigning one list variable to another and assuming that a second list has been created. In Python, assignment creates a reference to the existing list, not a copy. If two variables refer to the same list, modifying the list through either variable changes what the other variable sees.

refers torefers torefers tooriginal[1, 2, 3]one list objectalias[1, 2, 3]new list objectcopy
Do these two variables contain separate lists, or do they point to the same list object?

Tracing Shared References

original = [1, 2, 3] alias = original alias[0] = 99

Output
original
[99, 2, 3]

alias
[99, 2, 3]
performschangesvisible throughaliasalias[0] = 99[99, 2, 3]shared listoriginal[99, 2, 3]
How does a modification made through one variable reach and change the list observed through another variable?

The important debugging question is not only “Which variable did I change?” Ask instead, “Which list object does this variable refer to?” With assignment, original and alias are two names for one object. A mutation made through alias changes the object itself, so original observes the same change.

Making an Independent List

When the original list must remain unchanged, create a separate list before modifying it. The source identifies two ways to make a shallow copy: slice notation [:] and the list() constructor. Both create a new list with the same elements. A later modification to the copied list does not affect the original list.

python
Output
original
[10, 20, 30]

copy
[10, 200, 30]

Replacing a Slice

Slice assignment uses the form t[start:end] = sequence. It replaces the elements selected by the slice with the elements in the replacement sequence. Because the replacement sequence can have a different number of elements, slice assignment can also change the list's length.

t = ['a', 'b', 'c', 'd', 'e', 'f'] t[1:3] = ['x', 'y']

replaced byreplaced by0a1b1x2c2y3d
Which positions in the list are replaced when a slice such as t[1:3] receives a new sequence?

What do you think happens?

If a two-element slice is replaced by a three-element sequence, what happens to the list's length?

  • It stays the same
  • It decreases by one
  • It increases by one
Reveal answer

Answer: It increases by one.

The replacement sequence has one more element than the slice being replaced, so the list grows by one element.

Predicting Length Changes

To predict the result of slice assignment, compare two counts: the number of elements selected by the slice and the number of elements in the replacement sequence. Equal sizes preserve the list's length. A shorter replacement makes the list shrink. A longer replacement makes the list grow.

Selected sliceReplacement sequenceLength effect
Two elementsTwo elementsLength stays the same
Two elementsOne elementList shrinks
Two elementsThree elementsList grows

Compare the selected slice size with the replacement sequence size.

A copied list grows during slice assignment

Start with original = [10, 20, 30, 40, 50]. Create a separate copy, then replace the slice containing 30 and 40 with [200, 201, 202]. Predict both lists.

Create the copy: The copied list begins with the same five elements as original, but it is a separate list.

Identify the selected slice: The selected slice contains 30 and 40, so two elements will be replaced.

Compare sizes: The replacement sequence contains three elements, which is one more than the two selected elements.

Apply the replacement: The copied list grows from five elements to six elements. The original list is unchanged because the copy is separate.

original remains [10, 20, 30, 40, 50], while the copied list becomes [10, 20, 200, 201, 202, 50].

replace two with three[10, 20, 30, 40, 50]five elements[10, 20, 200, 201,202, 50]six elements
What happens to the elements and length when a two-element slice is replaced by three elements?

Debugging Checklist

  • Treating assignment as if it copied the list

    Both variables refer to the same list object, so a modification through alias changes what original sees.

    Fix: Use original[:] or list(original) when an independent list is needed.

  • Checking only the variable used for the mutation

    Another variable may refer to the same list and may therefore show the same change.

    Fix: Trace every variable that refers to the list before deciding whether the mutation is unexpected.

  • Assuming slice assignment preserves list length

    The replacement sequence is longer than the selected slice, so the list grows.

    Fix: Count the selected elements and the replacement elements before applying the assignment.

  • Copying when the program intentionally needs to update the original

    A copy prevents the original list from changing, which may not match the program's intended logic.

    Fix: Choose copying or in-place modification explicitly and document the reason for an intentional in-place change.

  1. Find every assignment involving the list.
  2. Mark which variables refer to the same list and which were made with a copy.
  3. Locate each mutation, including element assignment and slice assignment.
  4. For slice assignment, identify the selected elements and count the replacement elements.
  5. Decide whether the observed change is an intended in-place update or an unintended shared-reference mutation.

Try the Trace

MEDIUM

A program contains original = [1, 2, 3], alias = original, and alias[1:3] = [8]. Predict the value seen through original and decide whether the list grew, shrank, or stayed the same.

Hints
  • Assignment makes alias refer to the same list as original.
  • The slice [1:3] selects two elements.
  • The replacement sequence contains one element.
EASY

Rewrite the previous situation so that modifying the second list does not change original. Use one of the two copying techniques described in this article.

Hints
  • A slice with no start or end, such as original[:], creates a shallow copy.
  • The list() constructor can also create a separate list with the same elements.

Key Takeaways

  1. Assignment between list variables creates another reference to the same list; it does not create a copy.
  2. Use [:] or the list() constructor when you need a separate list with the same elements.
  3. Slice assignment replaces multiple selected elements and can change list length.
  4. A shorter replacement shrinks the list, an equal-sized replacement preserves its length, and a longer replacement grows it.
  5. When debugging a surprising mutation, trace shared references, the selected slice, and the size of the replacement sequence.

Key Takeaways

  • Assignment creates a shared reference rather than an independent list.
  • Copy a list with [:] or list() before modifying it when the original must be preserved.
  • Slice assignment updates a range of elements and may grow or shrink the list.
  • Debug unintended side effects by tracing which variables share a list and counting slice elements.