get + setdefault: почему выводится 3 3

get + setdefault: почему выводится 3 3

@python_quiz

Разберем этот квиз

Коротко: в выражении, где вторым аргументом у dict.get() вызывается dict.setdefault(), setdefault выполняется и добавляет ключ в словарь до того, как get проверит наличие ключа. В результате оба значения оказываются равны 3.

Код из задания

python

Пошаговый разбор

  1. Создается словарь: config = {"timeout": 10}.
  2. Python должен вычислить аргументы для вызова метода get. Перед вызовом самого get выражение config.setdefault("retries", 3) выполняется.
  3. config.setdefault("retries", 3):

- Поскольку ключ "retries" отсутствует, setdefault добавляет его в словарь со значением 3 и возвращает 3.

  1. Теперь вызов выглядит как config.get("retries", 3). Но к этому моменту в config уже есть ключ "retries": 3.
  2. dict.get("retries", 3) возвращает значение по ключу — 3 (а не дефолт), потому что ключ уже есть.
  3. Итого: value == 3 и config["retries"] == 3, печатается "3 3".

Демонстрация и сравнение вариантов

Оригинальный пример (поведение из квиза):

python

Если использовать get с литеральным дефолтом (без setdefault), ключ не создается:

python

Аналогичный эффект создания ключа при явном вызове setdefault:

python

Важные замечания и рекомендации

  • Встроенные функции/методы получают уже вычисленные аргументы: побочные эффекты, вызванные в аргументе, произойдут до выполнения самого метода.
  • dict.get(key, default) возвращает default только если ключа нет; но если вы передаёте в качестве default выражение с побочным эффектом, этот эффект выполнится в момент вычисления аргумента, даже если ключ уже существует в словаре (в данном случае побочный эффект — добавление ключа через setdefault).
  • Если вы не хотите, чтобы при запросе значения словарь модифицировался, не используйте побочные выражения в аргументе default. Предпочтительнее:

- передавать простой литерал как default (он не модифицирует словарь);
- явно проверить наличие ключа и при необходимости установить значение:
`python
if "retries" not in config:
config["retries"] = 3
value = config["retries"]
`
- или вызывать setdefault отдельно, если нужно именно гарантированно создать ключ:
`python
value = config.setdefault("retries", 3)
`

Вывод

В выражении с одновременным вызовом get и setdefault setdefault выполнится раньше и создаст ключ, поэтому и get вернёт значение этого ключа. Такое поведение удобно, но на практике лучше избегать скрытых побочных эффектов в аргументах — они затрудняют чтение кода и могут привести к неожиданным модификациям структуры данных.

Report Page