remove, discard и pop: удаление элементов множества
Три способа убрать элемент: remove падает на отсутствующем, discard молчит, pop достаёт произвольный. Один и тот же набор, три разные реакции на пустоту.
Редакция Питоники
Множество пополнили в прошлом уроке — теперь обратная сторона: элементы нужно уметь убирать. В Python для этого три метода, и различаются они не тем, что удаляют, а тем, как ведут себя, когда удалять нечего. remove падает с KeyError, discard молчит, pop к тому же возвращает удалённое значение. Выбор между ними — вопрос о смысле данных: нормально ли отсутствие элемента или это авария.
remove: убрать известный элемент
Метод remove принимает значение и вычёркивает его из множества. Убираем банан из фруктовой корзины:
fruits = {'яблоко', 'банан', 'киви'}
fruits.remove('банан')
print(sorted(fruits))
print(len(fruits))
['киви', 'яблоко'] 2
Два фрукта из трёх: remove вычеркнул значение, длина уменьшилась на единицу. Метод работает на месте, присваивать его результат не нужно — как и add из прошлого урока, он возвращает None.
remove на отсутствующем — KeyError
А теперь попросим remove убрать то, чего в множестве нет. Список в этом случае промолчал бы, у множества иная позиция: удаление несуществующего — происшествие:
fruits = {'яблоко', 'киви'}
fruits.remove('банан')
Traceback (most recent call last):
File "app.py", line 2, in <module>
fruits.remove('банан')
~~~~~~~~~~~~~^^^^^^^^^
KeyError: 'банан'KeyError — тот же тип ошибки, что у словаря при отсутствующем ключе: сообщение называет значение, которое не нашлось. Падение выглядит драматично, но в нём есть смысл: если программа пыталась удалить элемент, который обязан был существовать, — что-то пошло не так раньше, и тихое игнорирование спрятало бы поломку.
Когда отсутствие возможно и обрабатывается по плану, падение перехватывают: try обнимает рискованный вызов, except ловит KeyError и решает, что делать. Механизм try и except разобран в уроке про первую обработку ошибок, а здесь — рабочая связка для множеств:
fruits = {'яблоко', 'киви'}
try:
fruits.remove('банан')
except KeyError as e:
print('Поймали:', e)
print(sorted(fruits))
Поймали: 'банан' ['киви', 'яблоко']
Программа пережила попытку удаления: except поймал KeyError, а переменная e получила само недостающее значение — в кавычках, как его печатает сообщение ошибки. Множество осталось целым, и решение о дальнейшей работе теперь за вашим кодом.
discard: тихое удаление
Метод discard делает то же, что remove, но отсутствие значения не считает происшествием: нет элемента — и хорошо, удалять было нечего:
fruits = {'яблоко', 'киви'}
fruits.discard('банан')
print('после discard:', len(fruits))
fruits.discard('киви')
print(sorted(fruits))
после discard: 2 ['яблоко']
Первая строка попыталась убрать банан, которого нет, — и программа прошла дальше без единого слова. Вторая убрала существующий киви. Один метод на оба случая: discard не требует проверки перед удалением и не требует try.
s = {1, 2, 3}
s.discard(2)
s.discard(99)
print(sorted(s))
[1, 3]
Девяносто девять в множестве никогда не было, но discard принял вызов спокойно: длина уменьшилась ровно на одно — за двойку. Именно эту форму выбирают, когда список значений на удаление приходит из данных, где ничего нельзя гарантировать.
remove или discard: выбираем по задаче
Разница между методами — не вкус, а сообщение о смысле данных. Таблица сводит выбор к одному вопросу:
| Ситуация | Метод | Почему |
|---|---|---|
| Элемент обязан быть, его нет — баг | remove | KeyError сразу укажет на проблему |
| Элемент может отсутствовать — норма | discard | Тишина вместо лишнего try |
| Значение нужно ещё и получить | pop | Возвращает удалённый элемент |
| Проверить и решить самому | in + discard | Проверка in — тема урока 5 |
Правило одной фразой: если отсутствие элемента — ошибка данных, берите remove и ловите KeyError там, где знаете, что с ней делать. Если отсутствие — обычный ход событий, берите discard и не усложняйте.
pop: достать и удалить
Третий метод, pop, не принимает аргументов: он забирает из множества произвольный элемент и возвращает его, удаляя из набора. Произвольный — ключевое слово: множество не помнит порядка, и какой элемент выйдет первым, заранее не скажешь. Надёжный вывод получается там, где элемент один:
winner = {'команда-б'}
name = winner.pop()
print('достали:', name)
print('осталось:', len(winner))
достали: команда-б осталось: 0
Когда в множестве один элемент, спорить не о чем: pop вернул его и опустошил набор. Такой приём — работа с очередью из одного ожидаемого значения. А вот на большом множестве печатать результат pop в ожидаемом выводе нельзя: элемент непредсказуем, и проверять его некорректно.
Второе ограничение pop — пустое множество. Забирать нечего, и метод падает со своим собственным сообщением:
s = set()
try:
s.pop()
except KeyError as e:
print('Ошибка:', e)
Ошибка: 'pop from an empty set'
KeyError с текстом pop from an empty set — сообщение специфичнее, чем у remove: тут проблема не в конкретном значении, а в пустоте всего набора. Если множеству суждено пустеть, оборачивайте pop в try или проверяйте длину перед вызовом.
s = {1, 2}
try:
s.pop(0)
except TypeError as e:
print('Ошибка:', e)
Ошибка: set.pop() takes no arguments (1 given)
Ошибка честно говорит: pop у множества аргументов не принимает. Привычка из списков здесь не работает — и это нормально: у структур разное устройство, у каждого свои правила.
Удалять научились всеми тремя способами и выбирать между ними — по смыслу данных. Иногда вместо удаления нужно просто спросить: есть ли элемент внутри? Этот вопрос множества отвечают мгновенно, и этому посвящён урок про оператор in. А перехват KeyError там, где отсутствие — ожидаемое событие, подробно разобран в курсе про исключения.
Сначала предскажи ответ в голове — это главный навык программиста.
s = {1, 2, 3}
s.discard(99)
s.remove(1)
print(len(s))
print(sorted(s))
s = {'только'}
name = s.pop()
print(name)
print(len(s))
s = set()
try:
s.pop()
except KeyError as e:
print('Поймали:', e)
1. Что произойдёт при s.remove('x'), если 'x' нет в множестве?
2. Чем discard отличается от remove?
3. Что делает set.pop()?
4. Что произойдёт при pop() на пустом множестве?
5. Значение может отсутствовать, и это норма. Какой метод выбрать?
6. Что напечатает print('Поймали:', e) после except KeyError as e при s.remove('мур')?
Дано tasks = {'мыть', 'сушить', 'стирать'}. Уберите 'сушить' через remove, затем безопасно удалите 'гладить' (которого нет) через discard и напечатайте длину множества и отсортированный список задач.
Как удалить элемент из множества Python?
Тремя методами: remove(value) — с падением на отсутствующем, discard(value) — молча переживает отсутствие, pop() — достаёт произвольный элемент и удаляет его. Все три меняют множество на месте и возвращают None, кроме pop: он возвращает удалённое значение.
Урок 3: add и updateЧем discard отличается от remove?
Только реакцией на отсутствие значения. discard не считает отсутствие проблемой и молчит; remove бросает KeyError с недостающим значением в сообщении. Первое выбирают, когда отсутствие — норма, второе — когда отсутствие означает баг данных.
Когда remove падает с KeyError?
Всегда, когда удаляемого значения в множестве нет. Падение перехватывают связкой try и except KeyError as e — в переменную e попадёт само недостающее значение. А если отсутствие допустимо, проще сразу использовать discard.
Урок 2 курса про исключения: try и exceptЧто делает set.pop()?
Возвращает произвольный элемент множества и удаляет его. Аргументов метод не принимает — попытка s.pop(0) даёт TypeError. На пустом множестве pop падает с KeyError: 'pop from an empty set', поэтому перед вызовом стоит проверять len(s).
Как удалить элемент без риска падения?
Через discard: метод не требует ни проверки перед удалением, ни try. Альтернатива — remove под try и except KeyError, когда по отсутствию элемента нужно что-то сделать: напечатать, посчитать, переключить режим.
Понравился урок? Сошлитесь на него
«KeyError — тот же тип ошибки, что у словаря при отсутствующем ключе: сообщение называет значение, которое не нашлось.»
Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.
Что дальше
Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.
Что читать дальше
Множества · Урок 5
in в Python: мгновенная проверка принадлежности множеству
Один оператор, два механизма: in у множества отвечает мгновенно, у списка — перебором. Почему на миллионе элементов разница становится днями и секундами.
Множества · Урок 3
add и update в Python: добавление элементов в set
Множество собрано — теперь пополняем: add кладёт один элемент, update принимает списки, множества и даже строки. Дубликаты исчезают тихо, без ошибок.
Исключения · Урок 2
try и except: ловим ошибку и не падаем
Первый запасной план: try держит рискованный код, except — что делать, если он упал. Прыжки, пропуски и try внутри цикла — на живом коде.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
sqlite3 · Урок 8
UPDATE и DELETE в SQLite: меняем данные безопасно
Два самых ответственных оператора SQL: UPDATE и DELETE. Учимся менять и удалять строки так, чтобы не задеть чужие, — с rowcount как страховкой и мягким удалением.
удалить строку sqlмягкое удаление в базе данных
SQLAlchemy · Урок 8
Обновление и удаление записей
Данные записаны — теперь их надо менять: два пути SQLAlchemy, ручной через объект и массовый через update() и delete().
sqlalchemy удалить строку
Множества · Урок 6
Объединение множеств в Python: оператор | и union
Гости субботы плюс гости воскресенья — но каждый посчитан один раз. Оператор | и метод union собирают два множества в одно без дубликатов.
python объединение множествpython объединить множества