dict.pop и порядок вычисления аргументов в Python
@python_quizРазберем этот квиз
Коротко: в выражении d.pop('x', d['y']) Python сперва вычисляет все аргументы (включая d['y']), а уже потом вызывает метод pop. Если при вычислении default-значения произойдет исключение, вызов pop даже не начнется.
Пример из задания:

Что реально произойдёт

Поскольку d['y'] вызывает KeyError (ключа 'y' нет), исключение выбрасывается до вызова pop, и словарь остаётся неизменным.
Разбор возможных вариантов поведения (кратко)
- Вариант с
KeyErrorдля 'y' — соответствует фактическому поведению: default-аргумент d['y'] вычисляется заранее и вызывает исключение. - Вариант «удалит 'x' и result == 0; d станет {}» — неверен: удаление не произойдёт, потому что вычисление default-значения остановит выполнение раньше.
- Вариант «вернёт 0 (default), но d останется {'x': 0}» — некорректен по логике pop: если бы default использовался, это значило бы, что ключа нет, и словарь не менялся бы, но в нашем случае ключ есть; главное же — тут срабатывает исключение до вызова.
- Вариант «
KeyError: 'x' — pop бросит, потому что нет default» — неверен: default передан, но его вычисление приводит к ошибке раньше.
Почему так происходит
- В Python все аргументы функции (или метода) вычисляются до самого вызова. У pop нет «ленивого» механизма для default — это просто второй аргумент, вычисленный заранее.
Как сделать «ленивый» fallback правильно
Если вы хотите вычислять запасное значение только когда ключ отсутствует (или безопасно избежать ошибок при вычислении fallback), используйте один из шаблонов ниже.
1) Sentinel (один из самых универсальных способов)

Плюсы: не выполняем вычисление default заранее, однозначно отличаем «значение отсутствует» от возможного None/0 и т.д.
2) try/except (питонический, без двойного поиска)

Плюсы: минимально затратный вариант при частых успешных pop; атомарен для однопоточных сценариев.
3) Проверка наличия ключа (иногда подходит, но делает двойной доступ)

Минус: двухкратная проверка/доступ к словарю, что может быть нежелательно при высоких требованиях к производительности или в многопоточной среде.
4) Не путать с callable как default

Это редко удобно — легче использовать sentinel или try/except.
Дополнительные варианты
- collections.defaultdict — полезен для автоматического создания значений при обращении, но не для удаления (pop).
- Если вам нужен атомарный логически-несколько-операций в многопоточной программе — используйте блокировки.
Вывод
- Значение default в вызове pop вычисляется до вызова метода. В приведённом коде d['y'] вызовет
KeyErrorещё до попытки удаления 'x'. - Для ленивого вычисления fallback используйте sentinel+проверку или try/except (обычно предпочтительнее).