Python Slice Assignment: Modifying Sequences in Place

Python slice assignment is a powerful mechanism that allows developers to replace, insert, or delete elements in mutable sequences like lists directly in place without creating a new object. This article explains how slice assignment works under the hood, covering continuous slices, variable-length insertions, deletions, and the strict sizing rules required by extended step slices.

The In-Place Mechanism

When you assign a value to a slice of a mutable sequence, Python invokes the object's __setitem__ method with a slice object as the key. Unlike standard variable reassignment (a = [1, 2]), which changes the reference to a new object in memory, slice assignment alters the internal array of pointers of the existing sequence. The memory address (id()) of the sequence remains unchanged.

numbers = [1, 2, 3, 4]
original_id = id(numbers)

numbers[1:3] = [20, 30]

print(numbers)        # Output: [1, 20, 30, 4]
print(id(numbers) == original_id)  # Output: True

Continuous Slice Assignment ([start:stop])

Continuous slices do not require the replacement iterable to have the same length as the slice being replaced. Python automatically resizes the underlying array to accommodate the change.

Replacement

If the replacement sequence matches the length of the slice, elements are updated one-to-one:

items = ['a', 'b', 'c', 'd']
items[1:3] = ['x', 'y']
# Result: ['a', 'x', 'y', 'd']

Insertion and Expansion

If the replacement contains more items than the target slice, Python shifts subsequent elements to the right to make room:

items = ['a', 'd']
items[1:1] = ['b', 'c']  # Empty slice insertion at index 1
# Result: ['a', 'b', 'c', 'd']

Deletion and Shrinking

If the replacement contains fewer items or is an empty iterable, Python removes the sliced elements and shifts trailing elements to the left:

items = [1, 2, 3, 4, 5]
items[1:4] = []
# Result: [1, 5]

Using items[:] = [] clears the list while preserving references held by other variables.

Extended Slice Assignment ([start:stop:step])

When a step argument is provided and is not equal to 1, Python treats the operation as an extended slice. Unlike continuous slices, extended slices cannot resize the sequence.

The replacement iterable must contain the exact number of elements selected by the slice. If the lengths do not match, Python raises a ValueError.

numbers = [0, 1, 2, 3, 4, 5]

# Slice numbers[::2] selects 3 elements: indices 0, 2, and 4
numbers[::2] = [10, 20, 30]
# Result: [10, 1, 20, 3, 30, 5]

# This raises ValueError: attempt to assign sequence of size 2 to extended slice of size 3
# numbers[::2] = [10, 20]

Iterable Requirements

The right-hand side of a slice assignment must be an iterable (such as a list, tuple, generator, or range). Assigning a non-iterable directly to a slice raises a TypeError.

items = [1, 2, 3]
# items[0:2] = 10     # Raises TypeError: can only assign an iterable
items[0:2] = [10]     # Valid: replaces two items with one item -> [10, 3]