В Python аргументы по умолчанию вычисляются только один раз — в момент определения функции, а не при каждом её вызове. Это значит, что если в качестве значения по умолчанию для параметра используется изменяемый объект (например, список или словарь), он будет общим для всех вызовов функции, где этот аргумент явно не указан.
Пример:
def append_item(item, items=[]): items.append(item) return items print(append_item(1)) # [1] print(append_item(2)) # [1, 2], а ожидалось бы [2]
Правильный способ:
def append_item(item, items=None): if items is None: items = [] items.append(item) return items
Теперь каждый вызов получает свой список.
Вопрос: Что произойдет при многократном вызове функции со значением изменяемого объекта по умолчанию?
Ответ: Один и тот же объект изменяется каждый раз. Пример выше иллюстрирует это — список накапливает все значения.
История В крупном веб-приложении при кэшировании данных использовали функцию с параметром-словарём по умолчанию. Это приводило к тому, что данные между разными пользователями "утекали" друг к другу: кто-то менял свой профиль — и эти изменения иногда показывались другому пользователю из-за общего состояния глобального словаря.
История В тестах использовали функцию с изменяемым списком по умолчанию для сбора статы. Данные одного теста "перетекали" в другой, что приводило к неожиданным падениям, невозможности повторить баг и сложной отладке.
История В микросервисе для агрегации логов делали накопление событий с помощью функции, где аргумент по умолчанию был списком. Логи дублировались — временное накопление из старых запросов попадало к новым клиентам, что стоило часов расследований и потери данных.