Python
IndexError
List Manipulation
Append Method
Programming Tips

Why can't I build a list by assigning each element in turn? How can I add append the elements without getting an IndexError?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In Python, you cannot build a list by assigning to indices that do not yet exist. my_list[0] = "hello" on an empty list raises IndexError because index 0 does not exist. Unlike some languages where arrays auto-expand on assignment, Python lists require explicit methods to add elements: append(), insert(), extend(), or list comprehensions. This article explains why index assignment fails on empty lists and covers all the correct alternatives.

The Error

python
1my_list = []
2my_list[0] = "first"  # IndexError: list assignment index out of range
3
4# Same error with any index on an empty list
5my_list[5] = "fifth"  # IndexError: list assignment index out of range

Python lists are dynamically sized, but index assignment only works for existing positions. Assigning to my_list[i] requires that i is between -len(my_list) and len(my_list) - 1.

Fix 1: Use append()

python
1my_list = []
2my_list.append("first")
3my_list.append("second")
4my_list.append("third")
5print(my_list)  # ['first', 'second', 'third']
6
7# Build a list in a loop
8squares = []
9for i in range(10):
10    squares.append(i ** 2)
11print(squares)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

append() adds one element to the end of the list, growing it by one.

Fix 2: Use a List Comprehension

python
1# Most Pythonic way to build a list
2squares = [i ** 2 for i in range(10)]
3print(squares)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
4
5# With filtering
6even_squares = [i ** 2 for i in range(10) if i % 2 == 0]
7print(even_squares)  # [0, 4, 16, 36, 64]
8
9# From another iterable
10words = "hello world foo bar".split()
11upper = [w.upper() for w in words]
12print(upper)  # ['HELLO', 'WORLD', 'FOO', 'BAR']

List comprehensions are faster than append() loops and more readable for simple transformations.

Fix 3: Pre-Allocate with a Default Value

If you need index-based assignment, create the list with a known size first:

python
1# Pre-allocate with zeros
2n = 5
3my_list = [0] * n
4my_list[0] = 10
5my_list[3] = 40
6print(my_list)  # [10, 0, 0, 40, 0]
7
8# Pre-allocate with None
9slots = [None] * 10
10slots[7] = "data"
11print(slots)  # [None, None, None, None, None, None, None, 'data', None, None]

[0] * n creates a list of length n filled with zeros. Now my_list[i] = value works for any i in range(n).

Fix 4: Use insert() for Specific Positions

python
1my_list = ["A", "C", "D"]
2
3# Insert at a specific index (shifts existing elements right)
4my_list.insert(1, "B")
5print(my_list)  # ['A', 'B', 'C', 'D']
6
7# Insert at the beginning
8my_list.insert(0, "Start")
9print(my_list)  # ['Start', 'A', 'B', 'C', 'D']

insert(i, value) adds value before index i, shifting everything after it.

Fix 5: Use extend() for Multiple Elements

python
1my_list = [1, 2]
2my_list.extend([3, 4, 5])
3print(my_list)  # [1, 2, 3, 4, 5]
4
5# Equivalent with += operator
6my_list += [6, 7]
7print(my_list)  # [1, 2, 3, 4, 5, 6, 7]

Using a Dictionary When Indices Are Sparse

If you need to assign values to arbitrary indices without pre-allocating:

python
1# Dictionary as a sparse array
2data = {}
3data[0] = "first"
4data[100] = "hundredth"
5data[5] = "fifth"
6print(data)  # {0: 'first', 100: 'hundredth', 5: 'fifth'}
7
8# Convert to a list later if needed
9max_idx = max(data.keys())
10result = [data.get(i) for i in range(max_idx + 1)]
11print(result[100])  # 'hundredth'

Comparison of Methods

MethodUse CaseTime Complexity
append(x)Add to endO(1) amortized
insert(i, x)Add at positionO(n) — shifts elements
extend(iterable)Add multiple to endO(k) where k = len(iterable)
List comprehensionBuild from transformationO(n)
Pre-allocate [0]*nKnown size, index assignmentO(n) creation, O(1) assignment

Common Pitfalls

  • Assigning to indices on an empty list: my_list[i] = value only works if index i already exists. On an empty list, every index raises IndexError. Use append() to grow the list first.
  • Using append() to add a list instead of extend(): my_list.append([4, 5]) adds the list [4, 5] as a single element, creating a nested list. Use extend([4, 5]) to add the individual elements.
  • Pre-allocating with mutable defaults: [[]] * n creates n references to the same inner list. Modifying one modifies all. Use [[] for _ in range(n)] to create independent inner lists.
  • Off-by-one errors with pre-allocated lists: If you pre-allocate [0] * 5, valid indices are 0 through 4. my_list[5] = value still raises IndexError because index 5 does not exist in a 5-element list.
  • Using insert(0, x) in a loop for building a reversed list: Inserting at index 0 repeatedly is O(n^2) because each insert shifts all existing elements. Use append() and then reverse(), or build the list with append() and slice with [::-1].

Summary

  • Python lists do not auto-expand on index assignment — my_list[i] = x requires index i to already exist
  • Use append() to add elements one at a time to the end of a list
  • Use list comprehensions for building lists from transformations — faster and more Pythonic
  • Pre-allocate with [default] * n when you need index-based assignment at known positions
  • Use extend() or += to add multiple elements from an iterable
  • Use insert(i, x) to add at a specific position, but avoid it in loops due to O(n) cost per call

Course illustration
Course illustration

All Rights Reserved.