Мутируемые значения по умолчанию в Python
@python_quizРазберем этот квиз
Условие: дана функция, у которой параметр по умолчанию — список. Что выведет следующий код?

Короткий ответ: вывод будет

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

Обратите внимание: второй и третий вызовы продолжают накапливать элементы в том же списке.
Как безопасно задать "пустой" параметр по умолчанию
Если ожидается, что значение параметра должно создаваться заново при каждом вызове (например, пустой список), используйте невидимый маркер — обычно None — и создавайте новый объект внутри функции:

Это гарантирует, что при каждом вызове будет создан новый список.
Альтернативы:
- Если вам нужен неизменяемый контейнер по умолчанию — можно использовать кортеж.
- В некоторых случаях удобно явно копировать входной списка:
L = list(L)илиL = L.copy()(если требуется избежать побочных эффектов при передаче внешних объектов).
Когда поведение полезно
Иногда хочется именно сохранить состояние между вызовами (например, кэширование, аккумулирование значений). В таких случаях использование изменяемого значения по умолчанию — осознанный приём:

Но важно документировать такое поведение, чтобы пользователи функции знали о побочном эффекте.
Частые ошибки и подводные камни
- Не используйте трюк
L = L or []если вы хотите отличать пустые коллекции от других ложных значений (например,0,'',False) — это применимо только когда ожидается, чтоLлибо None, либо валидный список. - Для многопоточных программ изменения общего объекта требуют синхронизации (блокировки) — иначе возможны гонки.
- При использовании mutable по умолчанию в библиотечных функциях это может привести к труднонаходимым багам у пользователей.
Вывод
Поведение значений по умолчанию в Python — частый источник сюрпризов. Запомните простое правило: дефолтные изменяемые объекты создаются один раз при определении функции. Если нужен "свежий" объект при каждом вызове, используйте None (или другую явную инициализацию) внутри функции.