Урок курса

Список: создание, индексы и срезы

Python для новичков

До этого момента мы работали с отдельными значениями: числами, строками, булевыми — каждое из них хранилось в своей переменной. Но что если нужно держать вместе десяток цветов, список оценок или набор имён? Заводить отдельную переменную на каждый элемент — не вариант. Для этого в Python есть список.

Список как тип данных: создание литералом []

Список (list) — это упорядоченная коллекция элементов. Упорядоченная — значит, что у каждого элемента есть фиксированная позиция, и эта позиция не меняется сама по себе. Список может содержать что угодно: строки, числа, булевы значения, и даже всё вместе в одном списке.

Создаётся список литералом [] — квадратными скобками, внутри которых элементы перечисляются через запятую:

colours = ['красный', 'зелёный', 'синий']
numbers = [10, 20, 30]
empty = []
mixed = ['python', 42, True]

Что здесь происходит: colours — список из трёх строк, numbers — список из трёх целых чисел, empty — пустой список (полностью допустимое значение), mixed — список, где элементы разных типов.

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

Тип значения — list. Убедиться в этом можно так:

print(type(colours))  # <class 'list'>

Список присваивается переменной точно так же, как любое другое значение: справа — выражение, слева — имя переменной. Никаких специальных ключевых слов для этого не нужно.

Важная деталь: список — это изменяемый тип. Это означает, что после создания его можно модифицировать: добавлять и удалять элементы, менять их значения. Пока что мы только создаём список и смотрим на него как на готовую структуру — операции изменения будут темой следующего урока.

Обращение к элементу по положительному и отрицательному индексу

Когда список создан, нужно уметь добраться до конкретного элемента. Для этого используется индекс — целое число, которое указывает позицию элемента.

Позиции нумеруются с нуля. Первый элемент стоит на позиции 0, второй — на 1, и так далее. Запись lst[i] возвращает элемент на позиции i:

colours = ['красный', 'зелёный', 'синий']

print(colours[0])  # 'красный'
print(colours[1])  # 'зелёный'
print(colours[2])  # 'синий'

Нумерация с нуля — не случайность и не прихоть. Это стандарт, унаследованный от того, как адресная арифметика работает на уровне памяти. Привыкнуть к этому несложно, но поначалу легко ошибиться: когда хочется получить «первый» элемент, нужно писать [0], а не [1].

Отрицательные индексы — удобный способ считать с конца. Индекс -1 обозначает последний элемент, -2 — предпоследний, и так далее:

print(colours[-1])  # 'синий'
print(colours[-2])  # 'зелёный'
print(colours[-3])  # 'красный'

Под капотом отрицательный индекс просто прибавляется к длине списка: colours[-1] — это то же самое, что colours[len(colours) - 1]. Но писать [-1] короче и читается лучше, особенно когда длина списка заранее неизвестна.

Оба способа — положительные и отрицательные индексы — полностью равноправны. Какой использовать, зависит от задачи: если нужен элемент ближе к началу, удобнее положительный индекс; если ближе к концу — отрицательный.

IndexError: выход за пределы допустимых индексов

Индексы работают только в пределах списка. Для списка из n элементов допустимый диапазон — от 0 до n-1 для положительных индексов и от -n до -1 для отрицательных. Как только выходишь за эту границу, Python бросает IndexError: list index out of range.

Вот как это выглядит:

colours = ['красный', 'зелёный', 'синий']  # 3 элемента, индексы 0..2 и -3..-1

print(colours[3])   # IndexError: list index out of range
print(colours[-4])  # IndexError: list index out of range

Ошибка возникает в момент обращения — Python даже не пытается вернуть значение, сразу останавливает выполнение.

Чаще всего IndexError появляется в двух ситуациях.

Первая — классическая ошибка на единицу: человек думает «список из трёх элементов, значит последний на позиции 3», но последний на позиции 2. Нумерация начинается с нуля, поэтому последний элемент всегда n-1, а не n.

Вторая — обращение к элементу пустого списка:

empty = []
print(empty[0])  # IndexError: list index out of range

У пустого списка вообще нет допустимых индексов, любое обращение по индексу упадёт.

Как проверить, что индекс в пределах? Самый прямолинейный способ — сравнить с длиной списка перед обращением:

colours = ['красный', 'зелёный', 'синий']
i = 3

if i < len(colours):
    print(colours[i])
else:
    print('Индекс за пределами списка')

Здесь len(colours) возвращает 3, и условие 3 < 3 — ложное, поэтому обращения к несуществующему элементу не происходит.

Есть и отдельная точка внимания для отрицательных индексов: граница с другой стороны. Для списка из n элементов минимальный допустимый отрицательный индекс — -n. Всё, что меньше (например, -4 для трёхэлементного списка), тоже даёт IndexError.

Если видишь эту ошибку в коде — первым делом смотри на длину списка в момент обращения и на то, какой именно индекс использован. Чаще всего причина там.

Срез списка [start:stop:step]: синтаксис и примеры

Индекс даёт доступ к одному элементу. Срез — к нескольким сразу. Результатом среза всегда является новый список, а исходный остаётся нетронутым.

Синтаксис: lst[start:stop:step], где:

  • start — позиция, с которой начинается выборка (элемент включается);
  • stop — позиция, на которой выборка заканчивается (элемент не включается);
  • step — шаг между позициями.

Любой из трёх параметров можно опустить. По умолчанию start = 0, stop = конец списка, step = 1.

Разберём на конкретном списке:

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

print(lst[1:4])   # [1, 2, 3]  — элементы с позиции 1 до 3 (4 не входит)
print(lst[::2])   # [0, 2, 4]  — каждый второй элемент от начала до конца
print(lst[1:5:2]) # [1, 3]     — каждый второй в диапазоне 1..4

Граница stop не включается — это то же правило, что и в range(start, stop). Если написать lst[1:4], Python берёт позиции 1, 2, 3, но не 4. Поначалу это сбивает с толку, но логика одна: stop - start при шаге 1 всегда равно количеству элементов в срезе.

Опущенные параметры работают предсказуемо:

print(lst[:3])   # [0, 1, 2]      — от начала до позиции 2
print(lst[3:])   # [3, 4, 5]      — от позиции 3 до конца
print(lst[:])    # [0, 1, 2, 3, 4, 5]  — копия всего списка

lst[:] — стандартный способ сделать поверхностную копию списка. Оригинал и копия — два разных объекта.

Отрицательные границы в срезе работают так же, как отрицательные индексы при одиночном обращении — они считаются с конца:

print(lst[-3:])   # [3, 4, 5]  — последние три элемента
print(lst[:-2])   # [0, 1, 2, 3]  — всё, кроме последних двух

lst[-3:] удобно читать как «дай мне хвост длиной три элемента» — и длину списка при этом знать не нужно.

Отрицательный шаг разворачивает направление обхода. При step = -1 Python идёт от start к stop в обратную сторону, поэтому start должен быть правее stop (или оба опущены):

print(lst[::-1])     # [5, 4, 3, 2, 1, 0]  — весь список в обратном порядке
print(lst[4:1:-1])   # [4, 3, 2]  — от позиции 4 до позиции 2 включительно, назад

lst[::-1] — самый компактный способ перевернуть список. Он возвращает новый список в обратном порядке, исходный не трогает.

Важное отличие среза от обращения по индексу: срез никогда не бросает IndexError, даже если границы выходят за пределы списка. Python просто молча обрезает диапазон до допустимого:

print(lst[1:100])  # [1, 2, 3, 4, 5]  — stop обрезается до конца списка
print(lst[10:20])  # []  — диапазон вне списка, возвращается пустой список

Это полезно знать: если срез вернул пустой список, это не ошибка — просто диапазон не попал ни в один элемент.

Попробуйте решить

Дан список:

colours = ['красный', 'зелёный', 'синий', 'жёлтый']

Что вернёт colours[2]?

Продолжить с проверкой и прогрессом

Откройте интерактивный раннер с заданиями урока.

Перейти к интерактивному уроку