Дан фрагмент Python: "def foo(x=[]): x.append(1); return x" — объясните поведение функции при последовательных вызовах, причины этого поведения и способы избежать неожиданных эффектов

2 Мар в 08:35
21 +1
0
Ответы
1
Поведение
- При последовательных вызовах функция будет накапливать элементы в одном и том же списке, т.е.:
- первый вызов: результат \([\(1\)]\),
- второй: результат \([\(1\), \(1\)]\),
- третий: результат \([\(1\), \(1\), \(1\)]\).
- Причина: объект по умолчанию для аргумента xxx (список) создаётся один раз при определении функции, а не при каждом вызове; все вызовы используют одну и ту же ссылку (см. `foo.__defaults__`).
Пример (поведение):
def foo(x=[]):
x.append(111)
return x
print(foo()) # -> [111]
print(foo()) # -> [111, 111]
print(foo()) # -> [111, 111, 111]
Как избежать неожиданных эффектов
1. Использовать «маркер» `None` и создавать новый список внутри:
def foo(x=None):
if x is None:
x = []
x.append(111)
return x
2. Использовать уникальный маркер:
_sentinel = object()
def foo(x=_sentinel):
if x is _sentinel:
x = []
x.append(111)
return x
3. Не менять значение по умолчанию, а работать с копией:
def foo(x=None):
x = list(x) if x is not None else []
x.append(111)
return x
4. В специфичных случаях (когда нужно разделяемое состояние) — намеренно использовать мутируемый объект по умолчанию, но делать это осознанно и документировать.
Кратко: проблема в том, что дефолтные значения вычисляются один раз при определении; безопасный и общий способ — использовать `None` и создавать новый список внутри функции.
2 Мар в 09:10
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир