Мутируемые аргументы по умолчанию в Python

Мутируемые аргументы по умолчанию в Python

@python_quiz

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

Код из квиза — короткий и коварный:

python

Что выведет эта программа и почему?

Немедленный вывод

Запуск приведённого кода выведет:

Пояснение — почему именно так происходит.

Разбор механизма

  • Аргументы по умолчанию в Python вычисляются один раз — в момент определения функции, а не при каждом её вызове.
  • Если значение по умолчанию — изменяемый объект (например, список), то этот же объект будет использоваться при всех последующих вызовах функции, где аргумент не передан.
  • В нашем примере x при первом вызове — это пустой список, в который добавляется len(x), то есть 0 → список становится [0].
  • При втором вызове используется тот же самый список (уже [0]), поэтому len(x) равен 1, добавляется 1 → список становится [0, 1].

Можно убедиться в том, что используется один и тот же объект, распечатав его id:

python

Вы увидите одинаковые идентификаторы для обоих вызовов.

Анализ возможных результатов (вариантов ответа)

  • Вариант с выводом

`
[0]
[0, 1]
`
— соответствует описанному поведению, т.к. список по умолчанию общий для всех вызовов.

  • Вариант

`
[0]
[1]
`
был бы верен, если бы дефолтный список создавался заново при каждом вызове (но это не так).

  • Вариант

`
[0, 1]
[0, 1]
`
предполагал бы, что при первом вызове функция сразу добавляет и 0, и 1, либо что длина считается иначе — тоже не соответствует семантике кода.

  • Вариант с исключением не реализуется: операции с списком (append и len) корректны и не вызовут исключения в данном коде.

Как избежать «ловушки», если нужно новое значение по умолчанию при каждом вызове

Частая идиома — использовать None как маркер и создавать новый список внутри функции:

python

Теперь каждый вызов без аргумента получает новый список.

Когда такое поведение может быть полезным

Иногда именно сохранение состояния между вызовами полезно — например, для кэширования, аккумулирования результатов или реализации генераторов состояния. Главное — документировать намерение, чтобы код не вводил в заблуждение.

Итог — кратко

  • Аргументы по умолчанию вычисляются один раз при определении функции.
  • Если значение по умолчанию — изменяемый объект, он будет общим для всех вызовов.
  • Чтобы получить новый объект при каждом вызове, используйте None (или другой маркер) и создавайте объект внутри функции.

Report Page