Мутируемые аргументы по умолчанию в Python
@python_quizРазберем этот квиз
Условие (код из квиза):

Что выведет эта программа:

Пошаговый разбор
- При определении функции параметр buffer получает значение по умолчанию — и этот объект (список) создаётся ровно один раз в момент определения функции, а не при каждом вызове.
- Первый вызов collect(1) использует этот общий список по умолчанию: в него добавляется 1 → список становится [1], и печатается [1].
- Второй вызов collect(2, []) получает явно переданный новый список [], в который добавляется 2 → печатается [2]. Этот вызов не затрагивает список по умолчанию.
- Третий вызов collect(3) снова использует тот самый список по умолчанию, который уже содержит [1] после первого вызова; добавляется 3 → список становится [1, 3], и печатается [1, 3].
Почему это важно
Мутируемые объекты (например, list, dict, set) в качестве значений по умолчанию — частая ловушка для начинающих в Python. Ожидание, что значение по умолчанию каждый раз будет «свежим», неверно: объект создаётся один раз и разделяется между вызовами, которые не передают этот аргумент явно.
Разбор вариантов ответов (из квиза)
- Вариант, где выводы идут как
`
[1]
[2]
[3]
`
— ошибочен, потому что третий вызов использует тот же буфер, что и первый, а значит в нём уже есть 1.
- Вариант
`
[1]
[2]
[1, 3]
`
— совпадает с реальным поведением программы (см. объяснение выше).
- Вариант
`
[1]
[2]
[3, 1]
`
— неверен, порядок элементов в списке по умолчанию будет сохраняться в порядке добавления: сначала 1, потом 3 → [1, 3], а не [3, 1].
- Вариант
`
[1]
[2]
[1, 2, 3]
`
— тоже неверен: второй вызов использовал отдельный список, поэтому 2 не попал в список по умолчанию.
Как правильно избежать побочных эффектов
Стандартный шаблон — использовать None как значение по умолчанию и создавать новый список внутри функции:

Обратите внимание: теперь первый и третий вызовы не делят один и тот же список, потому что при каждом вызове без аргумента создаётся новый список.
Короткое резюме
- Значения по умолчанию вычисляются один раз при определении функции.
- Избегайте мутабельных значений по умолчанию, если вы не намерены намеренно разделять состояние между вызовами.
- Используйте паттерн с None, чтобы каждый вызов получал «чистый» объект по умолчанию.