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

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

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

remove, discard и pop: удаление элементов множества

Три способа убрать элемент: remove падает на отсутствующем, discard молчит, pop достаёт произвольный. Один и тот же набор, три разные реакции на пустоту.

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

Множество пополнили в прошлом уроке — теперь обратная сторона: элементы нужно уметь убирать. В Python для этого три метода, и различаются они не тем, что удаляют, а тем, как ведут себя, когда удалять нечего. remove падает с KeyError, discard молчит, pop к тому же возвращает удалённое значение. Выбор между ними — вопрос о смысле данных: нормально ли отсутствие элемента или это авария.

remove: убрать известный элемент

Метод remove принимает значение и вычёркивает его из множества. Убираем банан из фруктовой корзины:

remove убирает элемент
fruits = {'яблоко', 'банан', 'киви'}
fruits.remove('банан')
print(sorted(fruits))
print(len(fruits))
Вывод
['киви', 'яблоко']
2

Два фрукта из трёх: remove вычеркнул значение, длина уменьшилась на единицу. Метод работает на месте, присваивать его результат не нужно — как и add из прошлого урока, он возвращает None.

remove на отсутствующем — KeyError

А теперь попросим remove убрать то, чего в множестве нет. Список в этом случае промолчал бы, у множества иная позиция: удаление несуществующего — происшествие:

remove без защиты падает
fruits = {'яблоко', 'киви'}
fruits.remove('банан')
Вывод
Traceback (most recent call last):
  File "app.py", line 2, in <module>
    fruits.remove('банан')
    ~~~~~~~~~~~~~^^^^^^^^^
KeyError: 'банан'
Программа падает на удалении: банана в множестве нет. Путь к файлу в traceback будет вашим (показан условный app.py), а вид кареток чуть отличается между версиями Python.

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

Когда отсутствие возможно и обрабатывается по плану, падение перехватывают: try обнимает рискованный вызов, except ловит KeyError и решает, что делать. Механизм try и except разобран в уроке про первую обработку ошибок, а здесь — рабочая связка для множеств:

remove под try и except
fruits = {'яблоко', 'киви'}
try:
    fruits.remove('банан')
except KeyError as e:
    print('Поймали:', e)
print(sorted(fruits))
Вывод
Поймали: 'банан'
['киви', 'яблоко']

Программа пережила попытку удаления: except поймал KeyError, а переменная e получила само недостающее значение — в кавычках, как его печатает сообщение ошибки. Множество осталось целым, и решение о дальнейшей работе теперь за вашим кодом.

discard: тихое удаление

Метод discard делает то же, что remove, но отсутствие значения не считает происшествием: нет элемента — и хорошо, удалять было нечего:

discard молчит на отсутствующем
fruits = {'яблоко', 'киви'}
fruits.discard('банан')
print('после discard:', len(fruits))
fruits.discard('киви')
print(sorted(fruits))
Вывод
после discard: 2
['яблоко']

Первая строка попыталась убрать банан, которого нет, — и программа прошла дальше без единого слова. Вторая убрала существующий киви. Один метод на оба случая: discard не требует проверки перед удалением и не требует try.

discard не боится чужих значений
s = {1, 2, 3}
s.discard(2)
s.discard(99)
print(sorted(s))
Вывод
[1, 3]

Девяносто девять в множестве никогда не было, но discard принял вызов спокойно: длина уменьшилась ровно на одно — за двойку. Именно эту форму выбирают, когда список значений на удаление приходит из данных, где ничего нельзя гарантировать.

remove или discard: выбираем по задаче

Разница между методами — не вкус, а сообщение о смысле данных. Таблица сводит выбор к одному вопросу:

СитуацияМетодПочему
Элемент обязан быть, его нет — багremoveKeyError сразу укажет на проблему
Элемент может отсутствовать — нормаdiscardТишина вместо лишнего try
Значение нужно ещё и получитьpopВозвращает удалённый элемент
Проверить и решить самомуin + discardПроверка in — тема урока 5

Правило одной фразой: если отсутствие элемента — ошибка данных, берите remove и ловите KeyError там, где знаете, что с ней делать. Если отсутствие — обычный ход событий, берите discard и не усложняйте.

pop: достать и удалить

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

pop на множестве из одного элемента
winner = {'команда-б'}
name = winner.pop()
print('достали:', name)
print('осталось:', len(winner))
Вывод
достали: команда-б
осталось: 0

Когда в множестве один элемент, спорить не о чем: pop вернул его и опустошил набор. Такой приём — работа с очередью из одного ожидаемого значения. А вот на большом множестве печатать результат 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 или проверяйте длину перед вызовом.

попытка pop по индексу
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)
Проверь себя
0 / 6

1. Что произойдёт при s.remove('x'), если 'x' нет в множестве?

2. Чем discard отличается от remove?

3. Что делает set.pop()?

4. Что произойдёт при pop() на пустом множестве?

5. Значение может отсутствовать, и это норма. Какой метод выбрать?

6. Что напечатает print('Поймали:', e) после except KeyError as e при s.remove('мур')?

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

Дано tasks = {'мыть', 'сушить', 'стирать'}. Уберите 'сушить' через remove, затем безопасно удалите 'гладить' (которого нет) через discard и напечатайте длину множества и отсортированный список задач.

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

Как удалить элемент из множества 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-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Что дальше

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

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

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