Глава 4. Списки: коллекция баллов группы, срезы и рейтинг
Задача главы: научиться хранить баллы группы одним объектом — списком, — получать доступ к элементам по индексам и срезам, безопасно копировать список и строить из него отсортированный рейтинг.
4.1 Список баллов группы: создание, len(), .append(), .insert(), .remove(), del
Зачем это нужно. У группы из тридцати человек невозможно (и незачем) заводить тридцать отдельных переменных score_1, score_2, ... — вся аналитика встанет колом при первой же попытке посчитать средний балл. Список хранит любое количество баллов одним объектом, с которым можно работать целиком.
| Операция | Что делает | Ищет |
|---|---|---|
.append(x) | добавляет x в конец списка | — |
.insert(i, x) | вставляет x перед позицией i | по позиции |
.remove(x) | удаляет первое вхождение значения x | по значению |
del list[i] | удаляет элемент на позиции i | по позиции |
⚠️ Подводный камень
Список — изменяемый (mutable) объект. В отличие от строк из главы Строки и f-строки, методы .append(), .insert(), .remove() правят список на месте и возвращают None. Присваивание результата обратно в переменную не сохраняет изменения, а стирает весь список.
Traceback (most recent call last): File "report.py", line 6, in <module> group_scores.append(80)AttributeError: 'NoneType' object has no attribute 'append'| Ошибка | Что значит для Python | Человеческий перевод |
|---|---|---|
| — (тихая ошибка) | «.append() изменил список на месте и вернул None» | Присвоили результат .append() обратно в переменную — список подменился на None. .append(), .insert(), .remove() (и .sort() из раздела 4.4) работают на месте и ничего не возвращают: вызывайте их отдельной строкой, без =. |
AttributeError | «У None нет метода append» | Прямое следствие предыдущей ошибки: как только список стал None, любое обращение к нему как к списку падает. |
Правильный паттерн
Пояснение логики: список меняется методом сам по себе, поэтому переменной уже присвоен нужный объект — второе присваивание не только лишнее, но и разрушительное. Правило простое: если метод не печатает и не возвращает то, что логично было бы вывести, скорее всего он работает на месте.
4.2 Индексы и доступ к элементам: отрицательные индексы
Зачем это нужно. Нужно быстро достать конкретный балл — например, результат первого сдавшего или последней добавленной работы, — без просмотра всего списка.
Здесь и кроется главная путаница: Python нумерует элементы с нуля, а человек — с единицы. «Третья строка ведомости» — это group_scores[2], а не group_scores[3]. Перевод простой: индекс = номер_по_ведомости - 1.
⚠️ Подводный камень
len() возвращает количество элементов, а не индекс последнего. У списка из 5 элементов индексы — 0, 1, 2, 3, 4, и group_scores[5] (то есть group_scores[len(group_scores)]) уже не существует.
Traceback (most recent call last): File "report.py", line 3, in <module> last_score = group_scores[len(group_scores)]IndexError: list index out of range| Ошибка | Что значит для Python | Человеческий перевод |
|---|---|---|
IndexError | «Индекса 5 в списке из 5 элементов нет» | len(group_scores) равен 5, но последний индекс — 4. Смешали «сколько элементов» и «какой у последнего индекс». |
Правильный паттерн
Пояснение логики: отрицательные индексы существуют именно для «последнего», «предпоследнего» и так далее — считать длину вручную не нужно. А там, где номер приходит «по-человечески» (из ведомости, из формы), в индекс он превращается одним и тем же движением: - 1.
4.3 Срезы списка и копирование [:]
Зачем это нужно. Часто нужен не один балл, а часть списка — первые три сданные работы, последние поступившие, — а также независимая копия списка, чтобы попробовать на ней сортировку или удаление, не испортив исходную ведомость.
Правило то же, что и у срезов строк из главы Строки и f-строки: правая граница не включается, [start:stop] — от start включительно до stop не включая.
⚠️ Подводный камень
= не копирует список, а даёт ему второе имя. Список — изменяемый объект: обе переменные указывают на один и тот же участок памяти, и правка через любое из имён видна в обоих.
| Ошибка | Что значит для Python | Человеческий перевод |
|---|---|---|
| — (тихая ошибка) | «backup_scores — второе имя того же списка в памяти» | backup_scores = group_scores не создаёт новый объект, а даёт старому второй ярлык. Изменение через любое из имён меняет один и тот же список. Нужна отдельная копия — полный срез [:] или list(). |
Правильный паттерн
Пояснение логики: срез всегда создаёт новый список, даже если границы не заданы — [:] означает «от начала до конца», то есть все элементы, но уже сложенные в новый объект. Присваивание (=) — это не «сохранить копию», а «дать ещё одно имя»; копию делает только срез или list().
4.4 Сортировка и рейтинг: .sort() против sorted(), reverse=True, key=
Зачем это нужно. Рейтинг группы — это отсортированный список: от лучшего к худшему для похвальной грамоты, от худшего к лучшему для списка на пересдачу. У Python для этого есть два похожих по названию, но разных по поведению инструмента.
Параметр key= задаёт, по чему сравнивать элементы, — не только по значению «как есть»:
⚠️ Здесь key=len — готовая функция Python. Собственные функции для key= (например, сортировка по среднему баллу ученика) появятся в главе Функции.
⚠️ Подводный камень
Та же ловушка, что и с .append() в разделе 4.1: .sort() возвращает None, и присваивание результата обратно уничтожает список.
| Ошибка | Что значит для Python | Человеческий перевод |
|---|---|---|
| — (тихая ошибка) | «.sort() меняет список на месте и возвращает None» | Присвоили результат .sort() обратно в переменную — список стал None. Если нужен результат в отдельной переменной, а исходный порядок важен — используйте sorted(), а не .sort(). |
Вторая ловушка — сортировка списка со смешанными типами. В баллы иногда попадает текстовая заглушка вместо числа («не явился»), и сравнение str с int Python не умеет.
Traceback (most recent call last): File "report.py", line 3, in <module> mixed_scores.sort()TypeError: '<' not supported between instances of 'str' and 'int'| Ошибка | Что значит для Python | Человеческий перевод |
|---|---|---|
TypeError | «Оператор < не работает между str и int» | В списке баллов затесалось текстовое значение-заглушка. Сортировка требует однородных по сравнению элементов — сначала замените пропуски числом-маркером (например, 0) или уберите их из списка. |
Правильный паттерн
Пояснение логики: sorted() — функция общего назначения, всегда возвращает новый список, поэтому её безопасно присваивать переменной. .sort() — метод самого списка: он экономит память, не создавая копию, но подходит только тогда, когда исходный порядок точно больше не понадобится.
4.5 Мини-словарик ошибок четвёртой главы
| Ошибка | Что значит для Python | Человеческий перевод |
|---|---|---|
IndexError | «Индекс за пределами длины списка» | len(list) — это количество элементов, а не индекс последнего; берите list[-1]. |
AttributeError | «У None нет метода append/sort» | Присвоили результат метода, который возвращает None (.append(), .insert(), .remove(), .sort()), обратно в переменную. |
TypeError | «< не поддерживается между str и int» | В списке баллов оказалась текстовая заглушка вместо числа — сортировка требует однородных типов. |
| — (тихая ошибка) | «.append()/.sort() работают на месте и возвращают None» | Присваивание x = x.append(...) или x = x.sort() подменяет список на None. |
| — (тихая ошибка) | «= создаёт второе имя того же списка» | backup = original — не копия; правки видны в обоих именах. Для копии нужен original[:] или list(original). |
Самая коварная из тихих ошибок — общее владение списком через второе имя. Она срабатывает даже там, где явного .append() рядом нет:
Traceback здесь не появится — программа отработает и выдаст неверный результат молча. Единственная защита — привычка: если список передаётся дальше «для эксперимента» или «во временную переменную», копируйте его срезом [:] заранее, а не по факту обнаруженной пропажи данных.
Мини-кейс: рейтинг группы — топ-3 и отстающие по среднему баллу
Задача: по спискам ФИО и баллов группы посчитать средний балл, построить рейтинг топ-3 и выделить тех, кто не дотянул до среднего.
Средний балл группы: 71.3--- Топ-3 ---1 место: Смирнова Ольга — 922 место: Петров Пётр — 883 место: Иванов Иван — 78--- Требуют внимания (балл ниже среднего) ---Ким Юрий: 65 (ниже среднего на 6.3)Сидорова Анна: 45 (ниже среднего на 26.3)Фёдоров Глеб: 60 (ниже среднего на 11.3)Что получилось: пары (score, name) в scored_students не дали баллам и фамилиям разойтись при сортировке — sorted() сравнивает кортежи по первому элементу, поэтому балл остаётся ведущим ключом без отдельного key=. Топ-3 получен срезом готового рейтинга, а не написан вручную. Список отстающих собран отдельно от рейтинга: попадание в него зависит не от места, а от сравнения с average_score, поэтому в него мог бы попасть и человек с четвёртого места, и не попасть никто, если группа сильная.
⚠️ Если бы кто-то из группы не сдавал работу и его балл записали строкой 'н/я', sum(group_scores) упал бы с TypeError ещё на шаге расчётов — устойчивый разбор такой выгрузки ждёт в главе Ошибки и исключения.
Чек-лист главы
- Храню баллы группы в одном списке, а не в отдельных переменных
- Помню, что
.append(),.insert(),.remove(),.sort()меняют список на месте и возвращаютNone— не присваиваю результат обратно - Достаю элемент по индексу и помню, что нумерация в Python начинается с нуля
- Использую отрицательные индексы (
[-1]) для последнего элемента вместо ручного расчёта длины - Перевожу «человеческую» позицию в ведомости в индекс Python через
- 1 - Помню, что
len(list)— это количество элементов, а не индекс последнего, и не хожу поlist[len(list)] - Достаю часть списка срезами
[start:stop:step], помня, что правая граница не включается - Делаю независимую копию списка через
[:]илиlist(), а не через= - Знаю, что
backup = original— это второе имя того же списка, а не копия - Использую
sorted(), когда нужен новый список и важен исходный порядок, и.sort(), когда исходный порядок больше не нужен - Задаю направление сортировки через
reverse=True, а не переворачиваю список вручную - Понимаю, что делает
key=вsorted(), на простом примере (key=len) - Узнаю
TypeErrorпри сортировке списка со смешанными типами и знаю, что данные нужно сначала привести к одному типу
Дальше: Словари