Модуль 4 · Урок 4.2
Списки
Изменяемая последовательность: методы списка, проход по значениям и индексам, ссылки и копии, split и join.
В сквозной задаче числа пока были отдельными именами или вводились по одному. Для тысячи измерений нужна одна переменная на все. Список хранит много значений под одним именем и, в отличие от строки, меняется на месте. Отсюда и удобство, и главная ловушка: присваивание списка не копирует его. В уроке о значениях модель «имя ссылается на объект» была уточнением в «Тонкостях» — со списками без неё не обойтись.
Под рукой всё время будет список [4, 7, 2]: будем его менять, обходить, копировать и собирать из текста.
Список
Список хранит много значений под одним именем и, в отличие от строки, изменяется. Элементы списка пишут в квадратных скобках через запятую:
temperatures = [18, 21, 19, 23]print(temperatures[1])print(len(temperatures))Индексы и срезы работают похоже на строки. Главное различие: список изменяем. Он может хранить значения разных типов, но для обработки чисел обычно удобен однородный список.
numbers = [4, 7, 2]numbers[1] = 10numbers.append(8)print(numbers)append добавляет один элемент в конец: если передать ему список, он станет одним вложенным элементом.
Остальные действия со списком — каждый пример начинается с того же a = [4, 7, 2]:
- append
a.append(5):a→[4, 7, 2, 5]один элемент в конец- extend
a.extend([5, 6]):a→[4, 7, 2, 5, 6]элементы источника по одному- insert
a.insert(1, 9):a→[4, 9, 7, 2]перед позицией 1- pop
a.pop(): вернул2,a→[4, 7]удаляет и возвращает последний;a.pop(i)— элемент на позицииi- remove
a.remove(7):a→[4, 2]первое равное значение; ошибка, если его нет- del
del a[0]:a→[7, 2]удаляет по индексу- clear
a.clear():a→[]удаляет все элементы- reverse
a.reverse():a→[2, 7, 4]разворачивает на месте- index
a.index(7)→1, список не изменилсяиндекс первого равного; ошибка, если нет- count
a.count(7)→1, список не изменилсясколько раз встречается
Методы вроде append, extend и reverse меняют список и возвращают None. Поэтому a = a.append(5) теряет связь имени a со списком: результат метода — None.
a = [4, 7, 2]a = a.append(5)print(a)Обход списка
Список обходят по значениям, когда нужны элементы, и по индексам, когда нужны позиции.
a = [4, 7, 2]for value in a: print(value) for i in range(len(a)): a[i] *= 2print(a)Первый вариант удобен, когда нужны сами элементы. Второй — когда нужны позиции, соседние значения или замена элементов. Инструкция value *= 2 в первом цикле с целыми числами изменила бы только привязку имени value, а не список.
Не удаляй элементы из того же списка во время обычного прямого обхода, если не контролируешь смещение позиций: после удаления следующий элемент может быть пропущен. Проще собрать новый список подходящих элементов.
Два имени — один список
Присваивание не копирует список: два имени смотрят на один объект. Нажми «Запуск» и пройди пример по шагам: после b = a обе стрелки ведут к одному списку.
a = [1, 2]b = ab.append(3)print(a)b = a оба имени ведут к одному списку. b.append(3) меняет этот список, поэтому изменение видно и через a.a и b — два имени одного объекта, поэтому append через b меняет то, что видно через a. Чтобы создать отдельный внешний список, используют a.copy(), a[:] или list(a).
a = [1, 2]b = a.copy()b.append(3)print(a)print(b)print(a == b)print(a is b)b = a.copy() у каждого имени свой список: append через b не трогает список a.== сравнивает содержимое, is — один ли это список. Два разных списка могут быть равны по содержимому: сразу после копии a == b было бы True, а a is b — False. Сравнивай значения через ==; is нужен для value is None и для вопроса «это тот же список?».
Тонкости:is для чисел и строк
Python иногда хранит одинаковые небольшие числа и короткие строки одним объектом, а иногда — разными. Поэтому is для чисел и строк может давать разные ответы в разных программах; полагаться на это нельзя. Значения сравнивают только через ==.
Копия и вложенные списки
Копия внешнего списка не копирует вложенные списки. Список может содержать другие списки: так удобно представлять таблицу. Но копирование внешнего списка не копирует автоматически его внутренние элементы.
a = [[1, 2], [3, 4]]b = a.copy()b[0][0] = 99print(a)Созданы два внешних списка, но их элементы указывают на прежние общие строки таблицы. Такая копия называется поверхностной. Полную копию вместе с вложенными списками делает функция deepcopy стандартного модуля copy, но чаще проще создать каждую внутреннюю строку заново — как ниже.
Особенно опасна запись:
table = [[0] * 3] * 2table[0][0] = 9print(table)[[0] * 3] * 2: обе ячейки внешнего списка ссылаются на одну внутреннюю строку, поэтому изменение table[0][0] видно в обеих.Внешнее умножение повторило ссылку на одну строку. Создай каждую строку отдельно:
table = []for row in range(2): table.append([0] * 3)table[0][0] = 9print(table)[0] * 3 безопасно для последующей замены отдельных чисел: целые неизменяемы, а присваивание элементу заменяет ссылку. Проблема возникает при изменении повторённых вложенных объектов.
split и join
split превращает строку в список частей, join — список строк обратно в строку.
print(" 10 20 30 ".split())print("a,,b".split(","))Без аргумента разделителями служат серии пробельных символов, лишние края исчезают. С явным разделителем правило другое: два разделителя подряд дают пустую часть. Поэтому .split() и .split(" ") не взаимозаменяемы.
Разберём несколько чисел из одной строки без сокращений:
line = "10 20 30"parts = line.split()numbers = []for part in parts: numbers.append(int(part))print(numbers)Обратное соединение строк выполняет join:
words = ["Учимся", "писать", "программы"]print(" ".join(words))Объект перед точкой — разделитель. Элементы должны быть строками: целые сначала преобразуют. Для последовательного вывода чисел позже пригодится также print(*numbers).
Новый список
Новый список подходящих элементов оставляет исходный нетронутым. Из [5, -2, 0, 8, -1] создадим отдельный список неотрицательных значений:
source = [5, -2, 0, 8, -1]result = []for value in source: if value >= 0: result.append(value)print(result)print(source)Пустой результат допустим. Отдельное имя result сразу обозначает отдельный созданный список, а не вторую ссылку на исходный.
Сквозная задача
Сквозная задача курса: числа лежат в списке. Раньше числа 5, −2, 10, 7 были четырьмя именами, потом вводились по одному. Список хранит их все под одним именем, а for проходит по ним без ручного счётчика:
numbers = [5, -2, 10, 7]total = 0best = numbers[0]for x in numbers: total += x if x > best: best = xprint(total, best)Внутри цикла — те же две строки, что в уроке о while. Изменилось только то, откуда берутся числа.
Итог
Числа сквозной задачи теперь лежат в одном списке, а программа не зависит от их количества. Вот что из урока понадобится дальше.
- Список
[4, 7, 2]: индексы и срезы — как у строк, но элементы можно менять:a[i] = x. - Методы
append,insert,pop,remove,reverseменяют список и возвращаютNone: не пишиa = a.append(x). - Обход
for value in a— по значениям;for i in range(len(a))— когда нужны позиции или замена. b = aНе копия: два имени одного списка. Копия —a.copy(),a[:],list(a).== и is==— равное содержимое,is— тот же список.- ТаблицаСтроки создают отдельно (циклом), а не
[[0] * 3] * 2. split и joinsplit()режет строку на части," ".join(words)склеивает строки.
Проверьте себя
Ответь сначала сам, потом открой ответ.
Что напечатает программа?
a = [1, 2]b = ab.append(3)print(a)ОтветСкрыть ответ
[1, 2, 3]: b = a не копирует список, оба имени ведут к одному.
Почему цикл for value in a: value *= 2 не меняет список a? Как удвоить элементы?
ОтветСкрыть ответ
value *= 2 связывает с новым числом только имя value. Нужен перебор индексов: for i in range(len(a)): a[i] *= 2.
Что напечатает print([4, 7, 2].append(5))?
ОтветСкрыть ответ
None: append меняет список и возвращает None.
Что дадут "3;8;;1".split(";") и " 3 8 1 ".split()?
ОтветСкрыть ответ
['3', '8', '', '1'] и ['3', '8', '1'].
Что даст "-".join(["2026", "10", "10"])? Что случится с "-".join([2026, 10, 10])?
ОтветСкрыть ответ
2026-10-10; с числами — TypeError: элементы должны быть строками.
Практика
Для проверки ответов нужен JavaScript. Условия, подсказки и решения доступны ниже.
Напиши программу: неотрицательные значения
Напиши программу: из списка [3, -4, 0, 6, -2] она создаёт отдельный список неотрицательных значений и печатает его; исходный список не меняется. Запусти её и запиши, что она напечатала.
Запишите список так, как его печатает Python.
Подсказка
Начни с пустого списка и добавляй через append значения не меньше нуля.
Разбор
Например:
source = [3, -4, 0, 6, -2]result = []for value in source: if value >= 0: result.append(value)print(result)Ответ: [3, 0, 6]
Что напечатает программа?
a = [3, 1, 4]a.append(1)a.insert(0, 9)x = a.pop()a.remove(1)print(a)Запишите список так, как его печатает Python.
Подсказка
Проследи список после каждой строки. pop() забирает последний элемент, remove(1) удаляет первую единицу.
Разбор
[3, 1, 4, 1] → [9, 3, 1, 4, 1] → pop забирает последнюю 1: [9, 3, 1, 4] → remove(1) убирает оставшуюся единицу: [9, 3, 4].
Ответ: [9, 3, 4]
Что напечатает программа?
a = [1, 2, 3]b = ab[0] = 10print(a)Запишите список так, как его печатает Python.
Подсказка
b = a не копирует список: оба имени ведут к одному.
Разбор
Замена через b меняет тот же список, что виден через a. С b = a.copy() ответ был бы [1, 2, 3].
Ответ: [10, 2, 3]
Что напечатает программа?
a = [1, 2]b = [1, 2]print(a == b)print(a is b)Запишите напечатанные строки через пробел в порядке вывода.
Подсказка
== сравнивает содержимое, is — один ли это список.
Разбор
Содержимое равно, но это два разных списка.
Ответ: True False
Что напечатает программа?
t = [[0] * 2] * 3t[1][0] = 5print(t)Запишите список так, как его печатает Python.
Подсказка
Внешнее * 3 повторяет ссылку на один и тот же внутренний список.
Разбор
Все три строки — один объект, поэтому замена в одной видна во всех.
Ответ: [[5, 0], [5, 0], [5, 0]]
Что напечатает программа?
a = [5, 2, 8]a = a.reverse()print(a)Запишите напечатанное значение.
Подсказка
Методы, меняющие список, возвращают None.
Разбор
reverse развернул список, но вернул None, и a связалось с None.
Ответ: None
Напиши программу: отрицательные и сумма
Напиши программу: строка "4 -1 7 0 -3" превращается в список целых, затем программа печатает количество отрицательных чисел и сумму всех. Запусти её и запиши, что она напечатала.
Запишите два числа через пробел: количество отрицательных, затем сумму.
Подсказка
split() режет строку на части, int превращает каждую в число; счётчик отрицательных и сумма — в одном цикле.
Разбор
Например:
numbers = []for part in "4 -1 7 0 -3".split(): numbers.append(int(part))negative = 0total = 0for x in numbers: if x < 0: negative += 1 total += xprint(negative, total)Отрицательные −1 и −3; сумма 4 − 1 + 7 + 0 − 3 = 7.
Ответ: 2 7