Кейс на Python: def add_item(item, lst=[]): lst.append(item) ; return lst Объясните, почему использование изменяемого аргумента по умолчанию опасно, какие баги вызывает этот код и какие корректные альтернативы предложить

25 Фев в 09:39
28 +1
0
Ответы
1
Проблема: значение аргумента по умолчанию вычисляется один раз при определении функции, а не при каждом вызове. Для изменяемого объекта (например, списка) это значит, что все вызовы используют один и тот же объект — он накапливает состояние между вызовами.
Пример бага (поведение, которое часто неожиданно):
- при вызове add_item(111) функция вернёт [1][1][1];
- при последующем вызове add_item(222) вернётся уже [1,2][1, 2][1,2] — список не «сбросился».
Баги и последствия
- Непредсказуемое накопление данных между вызовами.
- Трудноотлавливаемые состояния в больших кодовых базах.
- Проблемы безопасности/утечка памяти при долгоживущих процессах.
- Потенциальные гонки при многопоточном использовании (разделяемый объект).
Правильные альтернативы
1) Стандартный паттерн — sentinel None:
def add_item(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst
2) Не мутировать входной аргумент — возвращать новый объект:
def add_item(item, lst=None):
if lst is None:
lst = []
return lst + [item]
3) Использовать неизменяемый default и конвертацию:
def add_item(item, lst=()):
return list(lst) + [item]
4) Если вам нужно намеренно хранить состояние между вызовами — делайте это явно (класс или замыкание), а не через «скрытый» default:
class Accumulator:
def __init__(self):
self.lst = []
def add(self, item):
self.lst.append(item)
return self.lst
Рекомендация: по умолчанию используйте None для изменяемых аргументов, чтобы при каждом вызове получать новый объект, если только явная общая память не требуется.
25 Фев в 09:48
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир