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

Что выведет этот код и почему он так себя ведёт — разберём подробно.
Короткий ответ
Код выведет:

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

Вы увидите одинаковые id для обоих вызовов, что подтверждает использование одного объекта.
Анализ вариантов
- "Выведет [[1], [1]] — каждый вызов использует новый список"
Не соответствует поведению Python для изменяемых дефолтных аргументов; список не создаётся заново при каждом вызове.
- "Выведет [[1], [1, 1]] — дефолтный список общий и мутируется"
Частично описывает идею мутации дефолтного списка, но результирующее представление значений в print([f(), f()]) не будет таким: так как оба элемента списка ссылаются на один и тот же объект, они оба отобразят текущее содержимое этого объекта после всех мутаций.
- "Выведет [[1, 1], [1, 1]] — оба элемента ссылаются на один и тот же изменённый список"
Соответствует истинному поведению: дефолтный список общий, оба места в итоговом списке ссылаются на него после двух добавлений.
- "Выбросит исключение из-за использования изменяемого аргумента по умолчанию"
Неверно — использование изменяемого аргумента по умолчанию само по себе не вызывает исключение.
Правильные практики (как избежать сюрпризов)
Если вам нужно, чтобы при каждом вызове создавался новый список, применяйте идиому с None:

Ещё вариант — использовать неизменяемые дефолты (например, кортежы) или функции-фабрики, но наиболее распространённое и понятное решение — проверка на None.
Вывод
Изменяемые объекты в качестве значений по умолчанию в Python создаются один раз при определении функции и затем переиспользуются при последующих вызовах. Это частый источник неожиданных багов, поэтому стоит либо явно документировать такое поведение, либо использовать паттерн с None для создания нового объекта при каждом вызове.