IndexError: list index out of range в Python — причины и как исправить

IndexError: list index out of range в Python - причины и как исправить Полезное

IndexError: list index out of range — исключение Python, которое возникает, когда программа обращается к элементу списка по индексу, которого в списке нет. У списка длины n допустимы индексы от 0 до n - 1 и отрицательные от -n до -1; все остальное вызывает эту ошибку. Ниже — как по traceback найти виноватое обращение, семь типовых причин с исправлением и выбор между проверкой if и try/except.

Все примеры проверены на Python 3.14.7 (24.09.2026). Подсвеченные «уголки» ~~~^^^ под строкой в traceback появились в Python 3.11; в более старых версиях будет только строка кода без подсветки.

Минимальный пример и как читать сообщение

scores = [85, 92, 78]
print(scores[2])
print(scores[3])

Первая строка печати выведет 78, вторая упадет:

78
Traceback (most recent call last):
  File "/w/a1.py", line 3, in <module>
    print(scores[3])
          ~~~~~~^^^
IndexError: list index out of range

Читать снизу вверх. Последняя строка — тип исключения и текст. Выше — строка кода и номер строки файла (line 3). Символы ^^^ указывают на конкретное обращение по индексу: в длинном выражении вроде data["users"][0]["phones"][0] они покажут, какой именно [...] не нашел элемента.

В списке три элемента, поэтому последний индекс — 2, а не 3. Индексация с нуля — главный источник этой ошибки.

Похожие сообщения: что именно сломалось

Текст после IndexError: подсказывает тип объекта и операцию. Не все похожие ситуации дают IndexError:

Выражение Результат в Python 3.14 Что значит
[1, 2, 3][-4] IndexError: list index out of range чтение по несуществующему индексу, в том числе отрицательному
items[0] = "x" при items = [] IndexError: list assignment index out of range запись в несуществующую позицию: список не растет от присваивания
[].pop() IndexError: pop from empty list удаление из пустого списка
(1, 2)[5] IndexError: tuple index out of range то же для кортежа
"abc"[10] IndexError: string index out of range то же для строки
{"a": 1}["b"] KeyError: 'b' у словаря ключи, а не индексы — другое исключение
[1, 2, 3][10:20] [] срез за границами не падает, а возвращает пустой список

Последняя строка таблицы важна: срез молча «проглатывает» выход за границы. Иногда это удобно, но может скрыть ошибку в логике.

Как найти причину за три шага

  1. По traceback найти строку и конкретное обращение [...] (подсказывают ^^^).
  2. Прямо перед этой строкой вывести индекс и длину: print(f"i={i}, len={len(scores)}"). Допустимый диапазон — 0..len-1.
  3. Понять, откуда взялся индекс: из счетчика цикла, из другого списка, из пользовательского ввода, из разбора строки. Ниже — типовые источники.

Семь типовых причин и исправление

1. Условие цикла <= вместо <

scores = [85, 92, 78]
i = 0
while i <= len(scores):
    print(scores[i])
    i += 1

Результат: напечатаются 85, 92, 78, затем IndexError: list index out of range на scores[i], когда i станет равно 3. Та же ошибка возникает с range(1, len(scores) + 1).

Исправление — строгое неравенство while i < len(scores):, а лучше перебор без индекса: for s in scores:. Если индекс все-таки нужен, используйте enumerate:

scores = [85, 92, 78]
for i, s in enumerate(scores):
    print(i, s)

Вывод: 0 85, 1 92, 2 78 — по паре на строке.

2. Два списка разной длины и общий индекс

names = ["Анна", "Борис", "Вера"]
ages = [21, 34]
for i in range(len(names)):
    print(names[i], ages[i])

Выведутся две пары, затем ошибка, и ^^^ укажет именно на ages[i]. Индекс взят по длине одного списка, а применен к другому.

Исправление — zip с strict=True (доступен с Python 3.10). Он не прячет расхождение, а сообщает о нем явно:

names = ["Анна", "Борис", "Вера"]
ages = [21, 34]
for name, age in zip(names, ages, strict=True):
    print(name, age)

Результат: две пары, затем ValueError: zip() argument 2 is shorter than argument 1. Это честнее, чем обычный zip без strict: тот просто остановится на коротком списке, и «Вера» тихо потеряется. Какой вариант нужен, зависит от задачи: если данные обязаны совпадать по длине — strict=True, если короткий список допустим — обычный zip.

3. Удаление элементов в цикле по индексам

nums = [1, 2, 2, 3, 2]
for i in range(len(nums)):
    if nums[i] == 2:
        del nums[i]

Результат: IndexError: list index out of range на nums[i]. range(len(nums)) посчитан один раз для длины 5, а список после каждого del укорачивается. Попутно цикл перепрыгивает через соседние элементы.

Исправление — собрать новый список вместо удаления на ходу:

nums = [1, 2, 2, 3, 2]
nums = [n for n in nums if n != 2]
print(nums)

Вывод: [1, 3]. Если нужно менять именно исходный объект, идите по индексам с конца: for i in range(len(nums) - 1, -1, -1): — удаление справа не сдвигает еще не просмотренные элементы, результат тот же [1, 3].

4. Разбор строки: полей меньше, чем ожидалось

line = "Иванов"
surname, name = line.split()[0], line.split()[1]

Результат: IndexError: list index out of range на line.split()[1]: в строке одно слово, и split() вернул список из одного элемента. Типичный случай при чтении файлов, CSV и input(), где встречаются пустые или неполные строки.

Исправление — проверить число полей до обращения:

for line in ["Иванов Иван", "Иванов", ""]:
    parts = line.split()
    if len(parts) < 2:
        print(f"Пропускаю строку {line!r}: ожидалось 2 поля, пришло {len(parts)}")
        continue
    surname, name = parts[0], parts[1]
    print(surname, name)

Вывод:

Иванов Иван
Пропускаю строку 'Иванов': ожидалось 2 поля, пришло 1
Пропускаю строку '': ожидалось 2 поля, пришло 0

5. Первый элемент пустого списка

Код вида results[0] или data["users"][0]["phones"][0] падает, когда поиск ничего не нашел или API вернул пустой массив. Проверка пустоты перед обращением:

data = {"users": [{"name": "Анна", "phones": []}]}
phones = data["users"][0]["phones"]
phone = phones[0] if phones else "не указан"
print(phone)

Вывод: не указан. Пустой список в условии ложен, поэтому if phones — короткая и понятная проверка.

6. Присваивание по индексу вместо добавления

items = []
items[0] = "первый"

Результат: IndexError: list assignment index out of range. В отличие от словаря, список не создает новую позицию при присваивании. Добавлять нужно через append (или insert), а если нужен список фиксированной длины — создать его заранее:

items = []
items.append("первый")
slots = [None] * 3
slots[2] = "третий"
print(items, slots)

Вывод: ['первый'] [None, None, 'третий'].

7. Перепутаны строки и столбцы в двумерном списке

grid = [
    [1, 2, 3],
    [4, 5, 6],
]
for i in range(3):
    for j in range(2):
        print(grid[i][j], end=" ")

Результат: напечатается 1 2 4 5, затем ошибка на grid[i] при i = 2: строк две, а не три. Размеры заданы числами «на глаз» и перепутаны местами.

Исправление — брать размеры из самих данных: for i in range(len(grid)): и for j in range(len(grid[i])): (так работает и с рядами разной длины). Проще всего — перебор без индексов: for row in grid: print(*row).

Проверка if или try/except

Когда индекс приходит извне (номер от пользователя, позиция из конфига) и может быть неверным, есть два подхода. Оба — рабочие, выбор зависит от ситуации.

def get_or_default(seq, index, default=None):
    """Элемент по индексу или default, если индекса нет (отрицательные тоже)."""
    if -len(seq) <= index < len(seq):
        return seq[index]
    return default

def get_eafp(seq, index, default=None):
    try:
        return seq[index]
    except IndexError:
        return default

scores = [85, 92, 78]
print(get_or_default(scores, 1), get_or_default(scores, 3), get_or_default(scores, -4, 0))
print(get_eafp(scores, 5, "нет"))

Вывод: 92 None 0 и нет. Обратите внимание на условие -len(seq) <= index: проверка только index < len(seq) пропустила бы индекс -4, и функция упала бы.

Подход Когда подходит На что смотреть
Проверка if (LBYL) выход за границы — ожидаемый нормальный случай, нужна понятная ветка учесть отрицательные индексы
try/except IndexError (EAFP) выход за границы редок, код короче держать в try только одно обращение, иначе поймаете чужой IndexError
Перебор без индекса, enumerate, zip обход коллекции целиком ошибка индекса невозможна по построению

Главное правило: try/except и значение по умолчанию уместны, когда отсутствие элемента — допустимая ситуация. Если индекс вне диапазона означает ошибку в логике (как в причинах 1-3 и 7), глушить исключение нельзя — нужно чинить расчет индекса, иначе программа тихо выдаст неверный результат.

Если не получилось

  • Ошибка появляется «иногда». Скорее всего, причина в данных: пустой ответ, неполная строка файла, короткий список. Выведите len(...) и сам объект перед падением на проблемном входе.
  • В traceback строка без ^^^. У вас Python до 3.11, подсветку отключили (-X no_debug_ranges) или, в Python 3.11-3.12, упавшее выражение занимает всю строку (например, items.pop() отдельной строкой): тогда подсветка не печатается, с 3.13 она есть и в этом случае. Разбейте длинное выражение на несколько переменных, чтобы номер строки указал на одно обращение.
  • Исправили <= на <, но падает дальше. Проверьте, не меняется ли список внутри цикла (причина 3) и не взят ли индекс от другого списка (причина 2).
  • Ошибка внутри библиотеки. Поднимитесь по traceback до последней строки из вашего файла — обычно туда передан пустой или короткий список.

Выводы

  • IndexError: list index out of range значит, что индекс вне диапазона -len..len-1; у списка из трех элементов последний индекс 2.
  • Traceback читается снизу вверх, а ^^^ (с Python 3.11) показывает, какое именно обращение по индексу упало.
  • Частые причины: <= в условии цикла, общий индекс для списков разной длины, удаление в цикле, неполные строки при разборе, пустой список, присваивание вместо append, перепутанные размеры матрицы.
  • Надежнее всего убрать ручной индекс: for x in seq, enumerate, zip(..., strict=True).
  • if-проверка или try/except — для внешних индексов; ошибку в логике расчета индекса ими не прячут.

Где применяется / связь с практикой

Освойте тему на практике

Ошибки индексации — часть базовой работы с коллекциями: разбор файлов и CSV, обработка ответов API, циклы по данным. Навык читать traceback и строить обход без лишних индексов экономит часы отладки. Системно разобрать списки, циклы, исключения и отладку на практических задачах можно на курсе «Python-разработчик. Базовый уровень». Отдельные темы по Python удобно посмотреть на бесплатных открытых уроках Otus.

FAQ

Почему numpy-массив или pandas дают другое сообщение?
Это другие типы с собственной индексацией: у массива NumPy текст ошибки другой (с указанием оси и размера), а в pandas обращение по метке может дать KeyError. Разбор выше относится к встроенному list.

Можно ли отключить эту ошибку, чтобы список сам возвращал None?
Встроенный list так не умеет. Можно написать функцию-обертку, как get_or_default выше, или унаследоваться от list, но обычно это маскирует ошибки логики.

Почему lst[-1] на пустом списке тоже падает?
Отрицательный индекс считается от конца, но элемент все равно должен существовать: у пустого списка нет ни первого, ни последнего элемента, поэтому [][-1] дает IndexError.

OTUS Журнал
Бесплатные открытые уроки (поп-ап)