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

20 Ноя в 08:27
3 +2
0
Ответы
1
Кратко: проблема в том, что значение аргумента по умолчанию вычисляется один раз при определении функции, и если это изменяемый объект (список), то все вызовы будут работать с одним и тем же объектом — его изменения (append) сохраняются.
Почему так происходит (механизмы языка):
- Значение дефолтного аргумента вычисляется при выполнении оператора def, не при каждом вызове.
- Если дефолт — изменяемый объект (list, dict и т.п.), то функция хранит ссылку на этот конкретный объект и при вызове использует его.
- Операция x.append изменяет объект «на месте», поэтому следующие вызовы увидят накопленные изменения.
Демонстрация (поведение оригинального примера):
def f(x=[]):
print(id(x))
x.append(111)
return x
f() # тот же id, возвращает [111]
f() # тот же id, возвращает [111, 111]
...
Как правильно (обычное решение — использовать None как «маркер» и создавать новый список при необходимости):
def f(x=None):
if x is None:
x = []
x.append(111)
return x
Альтернативы:
- использовать уникальный маркер: _sentinel = object(); def f(x=_sentinel): if x is _sentinel: x = []
- принимать неизменяемый дефолт (например, tuple) или копировать дефолт внутри функции: x = list(x) при необходимости.
Вывод: не используйте изменяемые объекты как значения по умолчанию; предпочтительный шаблон — дефолт None + создание нового объекта внутри функции.
20 Ноя в 08:33
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир