Поведение изменяемого дефолтного аргумента в Python
@python_quizРазберем этот квиз
Код, который нужно проанализировать:

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

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

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

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

Альтернативы и комментарии
- Иногда разумно явно передавать пустой список при вызове: f([]) — тогда каждый раз вы создаёте новый список, но это неудобно и не всегда явно отражает намерение.
- Для неизменяемых типов (int, str, tuple) похожая проблема обычно не проявляется, потому что их изменение не возможно — но логика повторного использования дефолтного объекта одинаково применима ко всем объектам.
Разбор вариантов ответов (кратко)
- Вывод [1] [1] — произошёл бы, если бы при каждом вызове создавался новый список (например, при шаблоне с
a=None). В данном коде этого нет. - Вывод [1] [1, 1] — именно это наблюдается: первый вызов формирует [1], второй использует тот же список и добавляет ещё один элемент.
- Вывод [1, 1] [1, 1] — это было бы в случае, если бы список уже содержал два элемента ещё до первого вызова или оба вызова возвращали ссылку на уже модифицированный список без промежуточного состояния; но для последовательных вызовов такого не происходит.
- Ошибка из-за изменяемого дефолтного аргумента — в Python это не ошибка, а часто источник тонких багов, но синтаксической/исполнительной ошибки не будет.
Вывод
Использование изменяемых значений по умолчанию (list, dict и т.п.) приводит к тому, что один и тот же объект разделяется между всеми вызовами функции без явного аргумента. Это часто неочевидно и может приводить к багам. Рекомендация — для изменяемых структур использовать None в качестве дефолта и создавать новый объект внутри функции.