Поведение изменяемого дефолтного аргумента в Python

Поведение изменяемого дефолтного аргумента в Python

@python_quiz

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

Код, который нужно проанализировать:

python

Короткий итог: программа выведет

text

Почему так происходит

  • Дефолтные значения аргументов в Python вычисляются единожды — в момент определения функции.
  • В данном случае объект списка [] создаётся однажды и используется как значение по умолчанию при каждом вызове f(), если явный аргумент не передан.
  • При первом вызове f() в этот список добавляется 1 -> список становится [1], и функция возвращает ссылку на этот же список.
  • При втором вызове f() используется тот же самый список (уже [1]), в него снова добавляется 1 -> [1, 1].

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

python

Пример типичного вывода (id будет вашим конкретным числом):

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

python

Альтернативы и комментарии

  • Иногда разумно явно передавать пустой список при вызове: f([]) — тогда каждый раз вы создаёте новый список, но это неудобно и не всегда явно отражает намерение.
  • Для неизменяемых типов (int, str, tuple) похожая проблема обычно не проявляется, потому что их изменение не возможно — но логика повторного использования дефолтного объекта одинаково применима ко всем объектам.

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

  • Вывод [1] [1] — произошёл бы, если бы при каждом вызове создавался новый список (например, при шаблоне с a=None). В данном коде этого нет.
  • Вывод [1] [1, 1] — именно это наблюдается: первый вызов формирует [1], второй использует тот же список и добавляет ещё один элемент.
  • Вывод [1, 1] [1, 1] — это было бы в случае, если бы список уже содержал два элемента ещё до первого вызова или оба вызова возвращали ссылку на уже модифицированный список без промежуточного состояния; но для последовательных вызовов такого не происходит.
  • Ошибка из-за изменяемого дефолтного аргумента — в Python это не ошибка, а часто источник тонких багов, но синтаксической/исполнительной ошибки не будет.

Вывод
Использование изменяемых значений по умолчанию (list, dict и т.п.) приводит к тому, что один и тот же объект разделяется между всеми вызовами функции без явного аргумента. Это часто неочевидно и может приводить к багам. Рекомендация — для изменяемых структур использовать None в качестве дефолта и создавать новый объект внутри функции.

Report Page