Mutable default arguments в Python: почему список накапливается

Mutable default arguments в Python: почему список накапливается

@python_quiz

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

Короткий разбор поведения функции с изменяемым аргументом по умолчанию и как его правильно избегать или использовать осознанно.

Исходный код

python

Что выведет этот фрагмент и почему — ниже разберём подробно.

Что происходит при выполнении

  • При определении функции значение аргумента по умолчанию вычисляется один раз — в момент определения функции. В данном случае создаётся один список и привязывается как значение по умолчанию для параметра users.
  • При первом вызове add_user('Ann') элемент 'Ann' добавляется в этот список, и функция возвращает ['Ann'].
  • При втором вызове add_user('Bob') функция использует тот же самый список (тот же объект), уже содержащий 'Ann', добавляет 'Bob' и возвращает ['Ann', 'Bob'].

Таким образом, вывод будет:

Можно дополнительно убедиться: если вызвать функцию ещё раз, список продолжит накапливаться.

python

Разбор вариантов (кратко)

  • Вариант "['Ann'] затем ['Bob']" не соответствует действительности — второй вызов не использует пустой список по умолчанию, он использует уже изменённый.
  • Вариант "['Ann'] затем ['Ann', 'Bob']" — это то, что действительно произойдёт при запуске приведённого кода.
  • Вариант "['Ann'] затем ['Bob', 'Ann']" неверен: порядок добавления сохраняется, добавление происходит в конец списка.
  • Вариант с исключением тоже неверен: повторное использование того же списка не приводит к исключению — это нормальное поведение, просто часто неожиданное.

Как правильно (без побочных эффектов)

Если вы хотите, чтобы при каждом вызове функция начинала с чистого списка по умолчанию, используйте значение None и создавайте новый список внутри функции:

python

Этот шаблон универсален и предотвращает накапливание значений между вызовами.

Когда поведение с накоплением нужно

Иногда именно требуется сохранять состояние между вызовами. Тогда можно явно сделать это явным и читаемым, например:

python

Или просто оставить список по умолчанию, но это чаще приводит к неожиданностям у читателей кода. Явное хранение состояния (атрибут функции, глобальная переменная, объект/класс) делает намерение понятным.

Итоги и рекомендации

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

Report Page