$ sudo teach IT
Модуль 5 · Коллекции · Урок 5.3

Списки

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

Теория + практика~35 минутНовичок

Зачем нужны списки

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

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

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

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

Создание списка: [] и list()

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

Официальный термин: запись [значение1, значение2, ...] называется литералом списка. Есть и второй способ — встроенная функция list(), которая превращает в список что-то другое: строку, диапазон range() или другую коллекцию.

grades = [5, 4, 5, 3, 5, 4]
fruits = ["яблоко", "банан", "вишня"]
empty = []

letters = list("hello")
numbers = list(range(1, 6))
Строка Что происходит
grades = [5, 4, 5, 3, 5, 4]Список из шести чисел через запятую в квадратных скобках. Порядок сохраняется в точности как написали.
empty = []Пустые квадратные скобки — список без единого элемента. Дальше в него можно что-то добавить.
list("hello")list() раскладывает строку на отдельные символы: получится ["h", "e", "l", "l", "o"].
list(range(1, 6))Превращает диапазон чисел от 1 до 5 включительно в список: [1, 2, 3, 4, 5].
Напоминание: обращение по индексу (fruits[0]) и срезы (fruits[1:3]) вы уже разбирали, когда изучали индексы и срезы строк — со списками они работают ровно так же: индексация с нуля, отрицательные индексы считают с конца, срез [начало:конец] не включает элемент с индексом "конец".

Добавление элементов: append, insert, extend

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

Термины: действие, которое можно выполнить над объектом (в данном случае — над списком), называется методом. Метод вызывают через точку после переменной: список.метод(аргументы).

cart = ["хлеб", "молоко"]

cart.append("сыр")
print(cart)

cart.insert(1, "яйца")
print(cart)

more_items = ["масло", "сахар"]
cart.extend(more_items)
print(cart)
Вызов Что делает Результат
cart.append("сыр")Кладёт ОДИН элемент в конец списка.["хлеб", "молоко", "сыр"]
cart.insert(1, "яйца")Вставляет элемент на указанную позицию (индекс 1), остальные сдвигаются правее.["хлеб", "яйца", "молоко", "сыр"]
cart.extend(more_items)Добавляет КАЖДЫЙ элемент другого списка по отдельности, а не сам список целиком.[..., "масло", "сахар"]
Ловушка: append([5, 6]) и extend([5, 6]) — не одно и то же. append положит один элемент — сам список [5, 6], и вы получите вложенный список внутри списка. extend "распакует" его и добавит числа 5 и 6 по отдельности. Если вы хотели объединить два списка, а получили в конце вложенный список — вы перепутали append и extend.

Удаление элементов: remove, pop, del, clear

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

Термины: remove(значение) ищет и удаляет первое совпадение по значению. pop(индекс) удаляет элемент по индексу и, в отличие от остальных способов, возвращает удалённый элемент. del список[индекс] — это не метод, а отдельный оператор языка, тоже удаляет по индексу, но ничего не возвращает. clear() удаляет всё содержимое, оставляя пустой список.

letters = ["a", "b", "c", "b", "d"]
letters.remove("b")
print(letters)

numbers = [10, 20, 30, 40]
removed = numbers.pop(1)
print(removed)
print(numbers)

del numbers[0]
print(numbers)

Разбор по шагам:

  • letters.remove("b") — ищет первое "b" в списке и убирает именно его. Второе "b" остаётся: ["a", "c", "b", "d"].
  • removed = numbers.pop(1) — удаляет элемент с индексом 1 (это 20) и сразу возвращает его в переменную removed. Печать removed покажет 20.
  • print(numbers) после pop покажет [10, 30, 40] — элемент с индексом 1 пропал.
  • del numbers[0] — удаляет элемент с индексом 0 (10), но ничего не возвращает, поэтому эту строку нельзя присвоить переменной. После неё numbers равен [30, 40].
Как выбрать способ: знаете значение — remove(). Знаете индекс и хотите получить удалённый элемент — pop(). Знаете индекс и возвращаемое значение не нужно — del. Нужно очистить список целиком, сохранив саму переменную — clear().

Поиск и подсчёт: in, index(), count(), len()

Простыми словами: иногда важно не изменить список, а просто спросить у него — есть ли там такое значение, на каком оно месте, сколько раз встречается и сколько в списке элементов вообще.

Термины: оператор in проверяет принадлежность и возвращает True или False. Метод index(значение) возвращает индекс первого совпадения. Метод count(значение) считает, сколько раз значение встречается. Функция len(список) вам уже знакома по строкам — для списка она возвращает число элементов.

nums = [3, 7, 2, 9, 7, 1]

print(7 in nums)
print(5 in nums)
print(nums.index(7))
print(nums.count(7))
print(len(nums))

По порядку: 7 in nums — True, семёрка в списке есть. 5 in nums — False, пятёрки нет. nums.index(7) вернёт 1 — это индекс ПЕРВОЙ семёрки (вторая, на индексе 4, не в счёт). nums.count(7) вернёт 2 — семёрка встречается дважды. len(nums) вернёт 6 — всего элементов.

Ловушка: если элемента в списке нет, index() не возвращает -1 и не молчит — он останавливает программу ошибкой ValueError. Перед вызовом index() для значения, в котором вы не уверены, сначала проверяйте его через in:
if 100 in nums:
    position = nums.index(100)

Сортировка: sort() и sorted()

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

Термины: sort() — метод, который сортирует список "на месте": меняет сам исходный список и ничего не возвращает (точнее, возвращает None). sorted(список) — встроенная функция, которая создаёт и возвращает НОВЫЙ отсортированный список, а исходный оставляет без изменений.

nums = [3, 1, 4, 1, 5]
nums.sort()
print(nums)

original = [3, 1, 4, 1, 5]
new_list = sorted(original)
print(original)
print(new_list)

desc = sorted(original, reverse=True)
print(desc)

Что произошло: nums.sort() отсортировал сам список nums — печать покажет [1, 1, 3, 4, 5]. А вот original после sorted(original) остался прежним: [3, 1, 4, 1, 5], потому что sorted() не трогает исходный список, а собирает новый и отдаёт его в new_list. Аргумент reverse=True у обоих способов разворачивает порядок на убывание.

Как выбрать: исходный порядок больше не нужен — берите sort(). Нужно сохранить оригинал и получить отдельный отсортированный список — берите sorted().

Копирование: почему b = a — ловушка

Простыми словами: когда вы пишете b = a для чисел или строк, у вас появляются два независимых значения. Со списками это правило неожиданно ломается — и это самая частая путаница у новичков.

Термин: список — изменяемый объект, и переменная хранит не сам список, а ссылку на него — место, где он лежит в памяти. Запись b = a не создаёт новый список, а создаёт второе имя для ТОГО ЖЕ списка. Изменение через одно имя видно через другое.

a = [1, 2, 3]
b = a
b.append(4)
print(a)
print(b)

c = a.copy()
c.append(99)
print(a)
print(c)

Разбор: b = a — теперь a и b смотрят на один и тот же список. b.append(4) меняет этот единственный список, поэтому и print(a), и print(b) покажут [1, 2, 3, 4], хотя изменяли через b. Дальше c = a.copy() создаёт настоящую, независимую копию. c.append(99) меняет только c: print(a) по-прежнему покажет [1, 2, 3, 4], а print(c) — [1, 2, 3, 4, 99].

Как получить независимую копию: b = a.copy() или срез b = a[:] — оба варианта создают новый список с теми же элементами, но никак не связанный с исходным.

Объединение и повтор: + и *

Простыми словами: два списка можно "склеить" в один новый, а один список — размножить несколько раз подряд.

Термин: оператор + между списками возвращает НОВЫЙ список из элементов обоих, не трогая исходные. Оператор * между списком и числом повторяет содержимое списка нужное число раз.

a = [1, 2, 3]
b = [4, 5, 6]
c = a + b
print(c)
print(a)

zeros = [0] * 5
print(zeros)

c = a + b создаёт новый список [1, 2, 3, 4, 5, 6], а a остаётся [1, 2, 3] — операция ничего не меняет в исходных списках. [0] * 5 повторяет список из одного нуля пять раз подряд: [0, 0, 0, 0, 0]. Этим приёмом удобно готовить заготовку нужного размера, например поле для игры.

Перебор списка через while

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

fruits = ["яблоко", "банан", "вишня"]

i = 0
while i < len(fruits):
    print(fruits[i])
    i += 1

Разбор: i = 0 — начинаем с первого индекса. Условие i < len(fruits) держит цикл, пока индекс не выйдет за границы списка. fruits[i] внутри тела берёт элемент под текущим номером. i += 1 в конце каждого прохода сдвигает счётчик на следующий индекс. Программа напечатает по очереди яблоко, банан, вишня.

Забегая вперёд: в Python есть более короткий и удобный способ перебора списков без ручного индекса — вы разберёте его в следующем уроке.

Вложенные списки — матрицы и таблицы

Простыми словами: список может хранить внутри себя другие списки. Так получают таблицы — например, шахматную доску или таблицу оценок, где есть и строки, и столбцы.

Термин: список, элементами которого являются другие списки, называют вложенным списком, а прямоугольную таблицу из чисел — матрицей. Чтобы попасть в конкретную ячейку, к списку применяют индекс дважды подряд: matrix[строка][столбец].

matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]

print(matrix[0][0])
print(matrix[1][2])

matrix[0][1] = 99
print(matrix[0])

Разбор: matrix — это список из трёх списков, каждый по три числа. matrix[0] берёт первую строку целиком, [1, 2, 3], а второй индекс [0] берёт из неё первый элемент — получаем 1. matrix[1][2] означает "вторая строка (индекс 1), третий элемент (индекс 2)" — это 6. Присваивание matrix[0][1] = 99 заменяет элемент внутри вложенного списка: строка с индексом 0 становится [1, 99, 3].

Частые ошибки

Ошибка 1. Спутать append и extend.

items = [1, 2, 3]
items.append([4, 5])
print(items)

Ожидали [1, 2, 3, 4, 5], а получили [1, 2, 3, [4, 5]] — список [4, 5] добавился как один-единственный элемент. Нужен был extend([4, 5]).

Ошибка 2. Забыть, что index() падает с ошибкой.

colors = ["красный", "синий"]
position = colors.index("зелёный")

Программа остановится с ValueError: 'зелёный' is not in list, потому что такого значения в списке нет. Перед вызовом index() для непроверенного значения используйте in.

Ошибка 3. Думать, что b = a создаёт копию.

original = [1, 2, 3]
backup = original
backup.append(4)
print(original)

Многие ждут, что original не изменится, ведь меняли backup. На деле выведет [1, 2, 3, 4] — backup и original это одно и то же место в памяти под двумя именами. Для независимой копии нужен original.copy().

Что важно запомнить

1. Список — упорядоченная и изменяемая коллекция. Создаётся через [] или list().
2. append добавляет один элемент в конец, insert — на конкретную позицию, extend — сразу все элементы другого списка.
3. remove(значение) удаляет по значению, pop(индекс) удаляет по индексу и возвращает элемент, del список[индекс] удаляет без возврата, clear() очищает список полностью.
4. in проверяет наличие значения, index() находит его позицию (и падает с ошибкой, если значения нет), count() считает повторы, len() — общее число элементов.
5. sort() сортирует список на месте и возвращает None; sorted() возвращает новый список, а исходный не трогает.
6. b = a НЕ копирует список — создаёт второе имя для того же списка. Для копии нужен a.copy() или a[:].
7. Вложенный список — список из списков. К ячейке обращаются двойным индексом: matrix[строка][столбец].

Проверьте себя

5 вопросов

Корзина покупок

Дана корзина покупок cart и каталог магазина: список товаров catalog_items и список цен catalog_prices (цена товара catalog_items[i] находится в catalog_prices[i], то есть у товаров и цен одинаковые индексы). Выполните по порядку четыре действия:

  1. добавьте в конец cart товар "сыр" (метод append);
  2. вставьте товар "яйца" на позицию с индексом 1 (метод insert);
  3. удалите из корзины первое вхождение товара "хлеб" (метод remove);
  4. посчитайте итоговую стоимость товаров, оставшихся в корзине, в переменную total — для каждого товара из cart найдите его позицию в catalog_items через .index() и возьмите цену с той же позицией в catalog_prices.

После каждого шага заготовка уже сохраняет копию корзины в отдельную переменную — эти строки менять не нужно.

Работа с матрицей

Premium

Второй по величине

Premium

Удалить дубли с сохранением порядка

Premium