Приведён фрагмент на Python: def foo(items=[]): items.append(1); return items; Объясните, почему вызовы foo() дают неожидимое поведение, как это исправить и какие принципы языка здесь задействованы

12 Янв в 18:09
9 +1
0
Ответы
1
Проблема: значение параметра по умолчанию создаётся один раз при определении функции и затем переиспользуется для всех вызовов. Если это изменяемый объект (список), изменения сохраняются между вызовами — откуда и «неожиданное» поведение.
Пример (исходный код):
def foo(items=[]):
items.append(1)
return items
Поведение при вызовах:
- первый вызов foo() возвращает [1][1][1],
- второй вызов foo() возвращает [1,1][1, 1][1,1],
- и т.д.
Причины (принципы языка):
- Значение аргумента по умолчанию вычисляется при определении функции, а не при каждом вызове.
- Списки — изменяемые объекты; операция append изменяет тот же объект, ссылка на который была сохранена в дефолтном аргументе.
- Следствие: побочные эффекты и общая мутабельность приводят к накоплению состояния между вызовами.
Как исправить (рекомендуемый способ):
def foo(items=None):
if items is None:
items = []
items.append(1)
return items
Теперь каждый вызов без аргумента создаёт новый список и возвращает [1][1][1].
Альтернативы:
- в качестве дефолта использовать неизменяемый объект (например, кортеж) или
- всегда создавать новый список внутри функции (например, `items = []` при условии `items is None`).
- в случаях с dataclass/типами полей — использовать `default_factory=list`.
Коротко: оценка дефолтов при определении + мутабельность объектов = совместное состояние между вызовами; решение — использовать None (сентинел) или неизменяемый дефолт.
12 Янв в 18:16
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир