Найдите и объясните проблему в этом Python‑кейсе:
def add_item(item, lst=[]):
lst.append(item)
return lst
Почему такое поведение встречает программистов и как его правильно протестировать и исправить

29 Сен 2025 в 10:30
19 +1
0
Ответы
1

Проблема в том, что параметр по умолчанию lst=[] — это изменяемый объект, который создаётся один раз при определении функции и потом переиспользуется при каждом вызове без явного аргумента. В результате successive вызовы накапливают элементы в одном и том же списке, что часто неожиданно.

Почему так происходит

Значение аргумента по умолчанию вычисляется один раз при определении функции (time of function definition), а не при каждом вызове.Если это изменяемый объект (список, dict, set и т.п.), то изменения внутри функции сохраняются между вызовами.

Пример демонстрации (поведение «до» исправления):
def add_item_bug(item, lst=[]):
lst.append(item)
return lst

print(add_item_bug(1)) # [1]
print(add_item_bug(2)) # [1, 2] <- неожиданно для многих
print(add_item_bug(3)) # [1, 2, 3]

Как правильно исправить
Использовать None (или другой «сентинел») в качестве значения по умолчанию и создавать новый список внутри функции:
def add_item(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst

Поведение после исправления:
print(add_item(1)) # [1]
print(add_item(2)) # [2]
print(add_item(3)) # [3]

Как правильно протестировать
1) Тест на накапливание (покажет баг):
def test_add_item_bug():
assert add_item_bug(1) == [1]
assert add_item_bug(2) == [1, 2] # ожидаемо неверное поведение, тест демонстрирует баг

2) Тест для исправленной версии (практически):
def test_add_item_fixed():
assert add_item(1) == [1]
assert add_item(2) == [2] # теперь новый список при втором вызове

3) Дополнительно проверить передачу собственного списка:
def test_add_item_with_explicit_list():
base = [10]
res = add_item(1, base)
assert res is base and res == [10, 1]

(эти тесты можно написать в unittest или pytest)

Дополнительные замечания

Иногда повторное использование мутируемого аргумента делается намеренно (например, кэширование). В этом случае такой код осознан и должен быть задокументирован.Для потокобезопасности общий список всё равно может приводить к гонкам — при параллельном доступе нужно синхронизировать или использовать другие структуры.Аналогичная проблема возникает с любыми изменяемыми значениями по умолчанию (dict, set, экземпляр класса и т.п.).

Краткий вывод
Нельзя использовать изменяемые объекты как значения по умолчанию, если не хотите, чтобы они разделялись между вызовами. Используйте None и создавайте новый объект внутри функции.

29 Сен 2025 в 11:45
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир