Модуль 3 · Списки и циклы

Справочник по Python · модуль 3 · списки и циклы

Глава 3.11

3.11

Методы списков:
удаление, поиск, сортировка

pop() извлекает элемент, clear() очищает список, index() и count() исследуют содержимое, а sort() меняет порядок элементов.

Категория
Методы list
Уровень
Начальный
Связанные темы
Индексы, удаление, сравнение
Зачем это знать
Извлекать, искать, считать и сортировать элементы

§1pop(): удалить и вернуть

pop() отличается от всех методов предыдущей главы тем, что возвращает значение, а не None. Он удаляет элемент и отдаёт его программе.

main.pyпоследний элемент
numbers = [10, 20, 30]
removed = numbers.pop()

print(numbers)
print(removed)
Выводсписок и извлечённое значение
[10, 20]
30

Без аргумента удаляется последний элемент. Именно поэтому removed = numbers.pop() — правильная запись, в отличие от numbers = numbers.append(...) из главы 3.10.

Извлечь — не то же самое, что удалить

Если извлечённое значение не нужно, результат можно не сохранять: numbers.pop() отдельной строкой просто удалит последний элемент.

§2pop() по индексу

pop(index) удаляет элемент с указанной позиции. Элементы правее сдвигаются влево, и их индексы уменьшаются на единицу.

main.pyудаление по индексу 1
numbers = [10, 20, 30]
removed = numbers.pop(1)

print(removed)
print(numbers)
Выводдвадцатка извлечена
20
[10, 30]
Схемасдвиг после удаления
было:  10(0)  20(1)  30(2)
стало: 10(0)  30(1)

Отрицательный индекс поддерживается: pop(-1) извлекает последний элемент. Если списка нет элемента с таким индексом — в том числе если список пуст, — возникает IndexError.

§3Список как стек

Пара append() и pop() превращает список в стек: последний добавленный элемент выходит первым. Такой порядок называют LIFO — last in, first out.

main.pyLIFO: последний вошёл — первый вышел
stack = []
stack.append(1)
stack.append(2)
stack.append(3)

print(stack.pop())
print(stack)
Выводизвлечена тройка
3
[1, 2]

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

§4Список как очередь

pop(0) извлекает первый элемент — получается очередь, где первым выходит тот, кто раньше пришёл (FIFO).

main.pyFIFO: первый вошёл — первый вышел
queue = ["Анна", "Борис", "Вера"]
first = queue.pop(0)

print(first)
print(queue)
Выводпервый обслужен
Анна
['Борис', 'Вера']

Работает это корректно, но не бесплатно: после удаления нулевого элемента все остальные сдвигаются на одну позицию. Для списка из миллиона элементов каждый pop(0) — миллион перемещений, тогда как pop() с конца обходится без них.

Когда очередь длинная

Для больших очередей в стандартной библиотеке есть collections.deque с методом popleft(): он удаляет первый элемент без сдвига остальных. В учебных задачах на десятки элементов разница незаметна, и pop(0) вполне уместен.

§5clear(): очистить список

clear() удаляет все элементы, сохраняя сам список.

main.pyпустой исходный список
numbers = [1, 2, 3, 4]
numbers.clear()

print(numbers)
print(len(numbers))
Выводэлементов нет, список есть
[]
0

§6clear() или новый список

На первый взгляд numbers.clear() и numbers = [] делают одно и то же. Разница проявляется, когда у списка два имени — как в главе 3.10.

main.pyочистка общего списка
numbers = [1, 2, 3]
another = numbers

numbers.clear()

print(numbers)
print(another)
Выводоба имени видят изменение
[]
[]
main.pyприсваивание вместо очистки
numbers = [1, 2, 3]
another = numbers

numbers = []

print(numbers)
print(another)
Выводстарый список остался у another
[]
[1, 2, 3]

clear() опустошает сам объект — его видят все имена. Присваивание numbers = [] создаёт новый пустой список и связывает с ним только имя numbers; прежний список продолжает жить под именем another.

Как выбрать

Нужно, чтобы изменение увидели все, кто работает с этим списком, — clear(). Нужно просто начать накопление заново в своей переменной — = [].

§7index(): найти позицию

index(value) возвращает индекс первого вхождения значения.

main.pyпервое вхождение числа 20
numbers = [10, 20, 30, 20]

print(numbers.index(20))
print(numbers.index(20, 2))
Выводпоиск сначала и с позиции 2
1
3

Второй аргумент задаёт позицию, с которой начинать поиск. Полная форма — index(value, start, stop); правая граница stop не включается, как и в range().

Если значения нет, возникает ValueError. Поэтому index() обычно ставят после проверки:

main.pyбезопасный поиск
numbers = [10, 20, 30]
value = 99

if value in numbers:
    print(numbers.index(value))
else:
    print("значение не найдено")
Выводошибки нет
значение не найдено

§8Все позиции значения

index() находит только первое вхождение. Если нужны все, список перебирают по индексам и собирают подходящие позиции.

main.pyсбор всех позиций
numbers = [10, 20, 30, 20]
positions = []

for index in range(len(numbers)):
    if numbers[index] == 20:
        positions.append(index)

print(positions)
Выводдвадцатка встречается дважды
[1, 3]

Здесь соединяются приёмы двух глав: перебор по индексам из 3.5 и накопление через append() из 3.2. Такая пара решает большинство задач «найти все, что подходит под условие».

§9count(): посчитать совпадения

count(value) проходит список и возвращает число равных элементов. Если совпадений нет, результатом будет 0, а не ошибка.

main.pyтри двойки
numbers = [1, 2, 2, 3, 2]

print(numbers.count(2))
print(numbers.count(9))
Выводсовпадения и отсутствие
3
0
Нужно узнатьЗаписьЕсли значения нет
есть ли значениеvalue in itemsFalse
сколько раз встречаетсяitems.count(value)0
на какой позицииitems.index(value)ValueError

Из трёх способов проверки только index() может остановить программу — это и делает его самым опасным.

§10sort(): сортировать на месте

sort() меняет порядок элементов исходного списка и возвращает None. По умолчанию числа идут по возрастанию.

main.pyдва направления
numbers = [5, 2, 9, 1]

numbers.sort()
print(numbers)

numbers.sort(reverse=True)
print(numbers)
Выводвозрастание и убывание
[1, 2, 5, 9]
[9, 5, 2, 1]

Аргумент reverse=True задаёт убывание. Python использует устойчивый алгоритм Timsort со сложностью порядка O(n log n) для общего случая: «устойчивый» означает, что равные элементы сохраняют исходный взаимный порядок.

§11Сортировка строк и ключ

Строки сортируются по алфавиту — точнее, по кодам символов.

main.pyимена по алфавиту
names = ["Вера", "Анна", "Борис"]
names.sort()
print(names)
Выводалфавитный порядок
['Анна', 'Борис', 'Вера']

Аргумент key — тема главы 6.10, но одну его форму удобно знать сразу. Он задаёт, по какому признаку сравнивать элементы. Например, по длине строки:

main.pyсортировка по длине
words = ["дом", "а", "книга"]
words.sort(key=len)
print(words)
Выводот короткого к длинному
['а', 'дом', 'книга']

Обратите внимание: у len нет скобок. Передаётся сама функция, а вызывать её для каждого элемента будет уже sort().

Заглавные буквы идут раньше строчных

Сравниваются коды символов, а не буквы «по-человечески». Поэтому "Яблоко" окажется раньше "арбуз". Чтобы регистр не влиял, используют names.sort(key=str.lower).

Смешивать несравнимые типы нельзя: [1, "a"].sort() вызовет TypeError — Python не знает, что больше, число или строка.

§12sort() или sorted()

ЗаписьИсходный списокРезультат
numbers.sort()изменяетсяNone
sorted(numbers)не изменяетсяновый отсортированный список
main.pyсохранение исходного порядка
numbers = [3, 1, 2]
ordered = sorted(numbers)

print(numbers)
print(ordered)
Выводдва списка
[3, 1, 2]
[1, 2, 3]

Разница та же, что между reverse() и [::-1] или между clear() и = []: метод меняет объект, функция строит новый. sorted() к тому же принимает любую коллекцию — строку, кортеж, множество — и всегда возвращает список.

§13Удаление внутри цикла

Есть ошибка, которую совершают почти все и которая не выдаёт никакого сообщения. Задача: убрать из списка все двойки.

main.pyтак делать нельзя
numbers = [1, 2, 2, 3, 2]

for number in numbers:
    if number == 2:
        numbers.remove(number)

print(numbers)
Вывододна двойка уцелела
[1, 3, 2]

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

Позиция циклаСписок сейчасВзят элементДействие
0[1, 2, 2, 3, 2]1пропуск
1[1, 2, 2, 3, 2]2удалена, список стал [1, 2, 3, 2]
2[1, 2, 3, 2]3вторая двойка пропущена: она сдвинулась на позицию 1
3[1, 2, 3, 2]2удалена первая двойка, список стал [1, 3, 2]
4[1, 3, 2]—элементов больше нет, цикл заканчивается

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

main.pyсборка нужного вместо удаления лишнего
numbers = [1, 2, 2, 3, 2]
kept = []

for number in numbers:
    if number != 2:
        kept.append(number)

print(kept)
Выводвсе двойки убраны
[1, 3]

Второй вариант — перебирать копию, а удалять из оригинала: for number in numbers.copy(). Тогда цикл идёт по неизменяемому набору, и сдвиги ему не мешают.

Общее правило

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

§14Способы удаления

ЗаписьЧто выбираетВозвращает удалённоеЕсли не найдено
pop(index)индекс, по умолчанию последнийдаIndexError
remove(value)первое равное значениенетValueError
del items[index]индекс или срезнетIndexError
clear()все элементынет—

Выбор простой: знаете позицию — pop() или del; знаете значение — remove(); нужно значение обратно — только pop().

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

Что написаноЧто произойдётКак правильно
items = items.sort()в items попадёт Noneitems.sort()
удалять элементы в цикле по тому же спискучасть элементов будет пропущена, ошибки не будетсобрать новый список или перебирать items.copy()
[].pop()IndexErrorпроверить, что список не пуст
items.index(value) без проверкиValueError, если значения нетсначала проверить value in items
ожидать все позиции от index()вернётся только перваяиспользовать цикл для всех совпадений
items.sort(key=len())TypeError: функция вызвана вместо передачиitems.sort(key=len)
сортировать вместе числа и строкиTypeError: их нельзя сравнитьсравнивать совместимые значения
numbers = [] для очистки общего спискадругие имена продолжат видеть старое содержимоеnumbers.clear()

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

Чем pop() отличается от remove()?

pop() удаляет по индексу и возвращает элемент, а remove() удаляет первое совпавшее значение и возвращает None.

Почему pop(0) медленнее, чем pop()?

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

Чем clear() отличается от numbers = []?

clear() опустошает сам объект, и это видят все его имена. Присваивание создаёт новый пустой список только для одной переменной.

Что вернёт count(), если совпадений нет?

Число 0. В отличие от index(), ошибки не будет.

Почему нельзя удалять элементы прямо в цикле по этому же списку?

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

Как отсортировать, не меняя исходный список?

Сохранить результат sorted(numbers) в новую переменную.

§17Шпаргалка

Извлечьvalue = items.pop(index)
Стек и очередьitems.pop()items.pop(0)
Очиститьitems.clear()
Найти и посчитатьitems.index(x)items.count(x)
Сортироватьitems.sort(reverse=True)
По признакуitems.sort(key=len)
Новый отсортированныйordered = sorted(items)

Итог главы

pop() удаляет и возвращает элемент, clear() очищает список, index() находит первую позицию, count() считает совпадения, а sort() сортирует исходный список. Для нового отсортированного списка используют sorted(). И главное правило безопасности: не менять длину списка, пока идёт цикл по нему.

Глава 3.11 · Методы списков. Часть 2 · Справочник по Python Автор: Макаров Максим Николаевич