Перейти к содержанию

412 уроков, 25 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 6 из 20 Начальный 20 мин 110 XP

Удаление: del, pop и popitem

Четыре способа убрать данные из словаря: del молча удаляет пару, pop отдаёт значение, popitem снимает последнюю пару, clear чистит всё. Плюс разбор, почему удалять во время обхода нельзя.

Редакция Питоники

В прошлом уроке пары появлялись и обновлялись присваиванием. Осталась зеркальная задача — убрать лишнее: товар снят с продажи, запись удалена, счётчик сброшен. У словаря четыре инструмента на все случаи: del, pop, popitem и clear. Отличаются они тем, возвращают ли значение и что делают с отсутствующим ключом: del удаляет пару молча, pop сначала отдаёт значение, и только потом забывает ключ.

del: удалить пару

del по ключу
stock = {'молоко': 12, 'хлеб': 5, 'сыр': 8}

del stock['хлеб']

print(stock)
Вывод
{'молоко': 12, 'сыр': 8}

Оператор del убирает пару целиком — и ключ, и значение, — и ничего не возвращает. Ключа 'хлеб' больше нет: обращение к нему теперь упало бы с KeyError из урока про строгий доступ. И у самого del тот же нрав: удалить отсутствующий ключ нельзя — словарь не догадывается, хотели вы что-то убрать или просто ошиблись.

pop: удалить и забрать значение

pop возвращает и удаляет
stock = {'молоко': 12, 'хлеб': 5, 'сыр': 8}

price = stock.pop('сыр')

print(price)
print(stock)
Вывод
8
{'молоко': 12, 'хлеб': 5}

Метод pop — это «достать и выбросить» одним движением: он вернул значение 8 и тут же удалил пару. Отсутствующий ключ pop тоже не прощает — но у него есть запасной путь, которого нет у del: второй аргумент. Он вернётся вместо ошибки, если ключа нет:

pop с умолчанием
stock = {'молоко': 12}

print(stock.pop('кефир', 0))
print(stock.pop('кефир', 'нет такого'))
print(stock)
Вывод
0
нет такого
{'молоко': 12}

popitem и clear: снять последнюю пару и опустошить всё

popitem и clear
stock = {'молоко': 12, 'хлеб': 5}

last = stock.popitem()
print(last)
print(stock)

stock.clear()
print(stock)
Вывод
('хлеб', 5)
{'молоко': 12}
{}

Метод popitem вызывается без аргументов: он снимает последнюю добавленную пару и отдаёт её кортежем из двух элементов — ключа и значения. Метод clear удаляет все пары разом, оставляя пустой словарь. popitem удобен, когда словарь обрабатывают как стопку: снял верхнюю пару — поработал с ней. clear — приём резковатый: он опустошает всё без разбора, поэтому чаще пары удаляют по одной — через del или pop, а проверку «а есть ли ключ вообще» делают оператором in из следующего урока.

popitem как стопка: снимаем всё по порядку

Раз popitem забирает последнюю добавленную пару, словарь превращается в стопку: работа идёт с конца. Это редко, но иногда нужно именно такое поведение — например, обработать задания в порядке поступствия и разбирать их с конца. Цикл с popitem выглядит так.

разбираем словарь как стопку
data = {'a': 1, 'b': 2, 'c': 3}

while True:
    try:
        key, value = data.popitem()
    except KeyError:
        break
    print(key, value)
Вывод
c 3
b 2
a 1

Пара-кортеж распаковывается прямо в две переменные — key, value = data.popitem(). Цикл заканчивается, когда словарь опустеет и popitem начнёт бросать KeyError: его ловим и выходим из цикла через break. Важно: в блоке except ничего не печатается: пустой словарь — это штатное завершение, а не ошибка.

Удаление во время обхода: почему нельзя

Отдельная грабли, на которую наступает почти каждый, кто впервые чистит словарь. Логика программиста простая: «просматриваю позиции, не годятся — удаляю». Python так не работает: он запрещает менять размер словаря посреди обхода и обрывает программу с RuntimeError. Причина техническая — обход держит в памяти указатель на текущую позицию таблицы, и удаление пары сдвигает всё следом, указатель уезжает мимо, и обход начинает повторять элементы или пропускать их.

удаляем правильно: сначала собираем, потом чистим
stock = {'молоко': 12, 'хлеб': 5, 'сыр': 8}

removed = []
for name in list(stock):
    if stock[name] < 6:
        removed.append(name)
        del stock[name]

print('удалено:', removed)
print(stock)
Вывод
удалено: ['хлеб']
{'молоко': 12, 'сыр': 8}

Обёртка list(stock) — обязательная деталь приёма: цикл идёт по копии ключей, и удаление из оригинала во время обхода уже безопасно. Собранные имена кладём в отдельный список removed — заодно получится готовый отчёт о том, что именно вычистили. Второй вариант — собрать новый словарь из нужных пар и просто заменить старый; оба подхода одинаково рабочие, первый чуть экономнее по памяти.

тот же обход без удаления
stock = {'молоко': 12, 'хлеб': 5, 'сыр': 8}

try:
    for name in stock:
        if stock[name] < 6:
            del stock[name]
except RuntimeError as e:
    print('RuntimeError:', e)
Вывод
RuntimeError: dictionary changed size during iteration

Вот как это выглядит «в лоб». Текст ошибки достаточно красноречив: dictionary changed size during iteration — размер словаря изменился во время итерации. Никакой номера строки и никакого имени ключа здесь нет, потому что это не ошибка данных, а ошибка самого способа обхода. Лечится только переносом удалений за пределы цикла.

del или pop: как выбрать

Возвращаясь к выбору инструмента. Правило простое: если удалённое значение больше не нужно — del, он короче и молчит; если значение надо куда-то записать перед удалением — pop, он возвращает его. Разница в одну строку, но на практике pop спасает от ситуации, когда значение забыли сохранить и потом пришлось искать его в другом месте.

pop забирает значение
stock = {'молоко': 12, 'хлеб': 5}

print(stock.pop('молоко'))
print(stock)
print(stock.pop('молоко', 'нет'))
Вывод
12
{'хлеб': 5}
нет

Третий вызов — уже другой случай: ключа 'молоко' в словаре нет, и вместо KeyError вернулось умолчание 'нет'. Строка вместо числа выбрана специально, чтобы её было видно в выводе. В реальном коде подставляют то, что соответствует типу: ноль для чисел, пустую строку для текста.

Второй способ почистить: собрать новый словарь

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

чистка через генератор словаря
stock = {'молоко': 12, 'хлеб': 5, 'сыр': 8}

fresh = {name: amount for name, amount in stock.items() if amount >= 6}

print(fresh)
print(stock)
Вывод
{'молоко': 12, 'сыр': 8}
{'молоко': 12, 'хлеб': 5, 'сыр': 8}

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

Иногда удалять не нужно вовсе. В одном сервисе заказ нельзя было убрать из словаря: его статус смотрели три разных обработчика, и если бы пара исчезла, обработчик решил бы, что заказа не существует, и списал его повторно. Там оставили в словаре флаг deleted и пропускали такие записи при обходе.

Что дальше

Удалять пары вы теперь умеете четырьмя способами и знаете, почему нельзя удалять прямо в цикле. Дальше — проверка наличия ключа: оператор in отвечает на вопрос «есть ли такой ключ» и никогда не падает. В уроке 7 разбираем его, сравниваем с get и объясняем, почему поиск по ключу быстрый даже на огромных словарях.

Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

stock = {'чай': 3, 'кофе': 1}

price = stock.pop('чай')
print(price)
print(stock)
d = {'a': 1}

print(d.pop('b', 100))
print(d)
d = {'x': 1, 'y': 2}

del d['x']
print(d)

d.clear()
print(d)
Проверь себя
0 / 8

1. Что делает del stock['хлеб']?

2. Чем pop отличается от del?

3. Что вернёт stock.pop('кефир', 0), если ключа 'кефир' в словаре нет?

4. Что вернёт popitem() для словаря {'молоко': 12, 'хлеб': 5}?

5. Что останется в словаре после stock.clear()?

6. Что произойдёт, если удалять пары прямо во время цикла for по словарю?

7. Как удалить пары, не ломая обход?

8. Что вернёт popitem() на пустом словаре?

Карточки терминов
Запомнено: 0 / 8
Практика

Дан словарь stock = {'молоко': 12, 'хлеб': 5, 'сыр': 8}. Удали хлеб через del, затем достань и удали сыр одним методом, сохранив цену в переменную. Выведи цену сыра и оставшийся словарь.

practice.py
Вопросы и ответы по уроку

Как удалить элемент из словаря в Python?

По ключу — оператором del: del stock['хлеб'] убирает пару целиком. Если удалённое значение ещё нужно, берите метод pop: price = stock.pop('сыр') вернёт значение и удалит пару. Очистить весь словарь разом можно методом clear.

Урок 5: добавление и изменение пар

Что лучше: del или pop?

Зависит от того, нужны ли данные. Если значение больше не понадобится — del короче и честнее: пара исчезает молча. Если значение надо куда-то записать перед удалением — pop делает это одним движением и, в отличие от del, умеет не падать на отсутствующем ключе благодаря второму аргументу.

Что будет, если удалить несуществующий ключ?

del и pop без умолчания уронят программу с KeyError — словарь не отличает «нечего удалять» от опечатки. Если ключ может отсутствовать, используйте pop с запасным значением: stock.pop('кефир', 0) — или сначала проверьте наличие ключа оператором in.

Какая пара удаляется методом popitem?

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

Почему нельзя удалять пары во время перебора словаря?

Обход держит указатель на текущую позицию внутренней таблицы, а удаление пары сдвигает всё следом, и указатель уезжает мимо. Python запрещает такое и обрывает программу с RuntimeError: dictionary changed size during iteration. Спасает приём с list(prices.keys()): цикл идёт по копии ключей, удаление из оригинала во время обхода становится безопасным.

Урок 8: перебор словаря

Когда лучше pop, а когда del?

del — когда удалённое значение больше не нужно: короче и ничего не возвращает. pop — когда значение надо сохранить в переменную перед удалением: stock.pop('сыр') вернёт его и уберёт пару. Если ключ может отсутствовать, у pop есть козырь: второй аргумент-умолчание, которого нет у del.

Чем clear отличается от удаления по одной паре?

clear опустошает словарь целиком и не возвращает ничего — все пары исчезают разом. Точечное удаление через del или pop выборочное и часто сопровождается отчётом о том, что именно убрали. clear удобен, когда старые данные больше не нужны, а сам объект словаря ещё понадобится.

Понравился урок? Сошлитесь на него

«del удаляет пару молча, pop сначала отдаёт значение, и только потом забывает ключ.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Что дальше

Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.