Для функции на Python `def add_item(x, lst=[]): lst.append(x); return lst` объясните, почему поведение может показаться неожиданным, и предложите корректные варианты реализации с объяснением причин
Проблема: значение дефолтного аргумента вычисляется один раз при определении функции, поэтому один и тот же список используется при всех вызовах. Из‑за этого последовательные вызовы накапливают элементы в одном списке. Пример неожиданного поведения: Код (ваша функция) def add_item(x, lst=[]): lst.append(x) return lst Вызовы: add_item(1) -> [1][1][1] add_item(2) -> [1,2][1, 2][1,2] (ожидаем [2][2][2], но список общий) Корректные варианты 1) Стандартный (рекомендуемый) — использовать `None` и создавать новый список внутри: def add_item(x, lst=None): if lst is None: lst = [] lst.append(x) return lst Пояснение: дефолт `None` неизменяем и не разделяется между вызовами; новый список создаётся при каждом вызове, если аргумент не передан. 2) Функциональный (не меняет переданный список, возвращает новый список): def add_item(x, lst=None): if lst is None: lst = [] return lst + [x] Пояснение: возвращается новый список (операция `+` создаёт копию), полезно когда не хотите мутировать входной `lst`. 3) Использовать уникальный sentinel, если `None` может быть законным значением: _sentinel = object() def add_item(x, lst=_sentinel): if lst is _sentinel: lst = [] lst.append(x) return lst Пояснение: применимо если `None` — допустимый аргумент и нужно различать "не передан" и "передан None". Короткая сводка: не используйте изменяемые объекты (например [][][]) как дефолтные значения; вместо этого создавайте их внутри тела функции (через `None` или sentinel) или возвращайте новую коллекцию.
Код (ваша функция)
def add_item(x, lst=[]):
lst.append(x)
return lst
Вызовы:
add_item(1) -> [1][1][1]
add_item(2) -> [1,2][1, 2][1,2] (ожидаем [2][2][2], но список общий)
Корректные варианты
1) Стандартный (рекомендуемый) — использовать `None` и создавать новый список внутри:
def add_item(x, lst=None):
if lst is None:
lst = []
lst.append(x)
return lst
Пояснение: дефолт `None` неизменяем и не разделяется между вызовами; новый список создаётся при каждом вызове, если аргумент не передан.
2) Функциональный (не меняет переданный список, возвращает новый список):
def add_item(x, lst=None):
if lst is None:
lst = []
return lst + [x]
Пояснение: возвращается новый список (операция `+` создаёт копию), полезно когда не хотите мутировать входной `lst`.
3) Использовать уникальный sentinel, если `None` может быть законным значением:
_sentinel = object()
def add_item(x, lst=_sentinel):
if lst is _sentinel:
lst = []
lst.append(x)
return lst
Пояснение: применимо если `None` — допустимый аргумент и нужно различать "не передан" и "передан None".
Короткая сводка: не используйте изменяемые объекты (например [][][]) как дефолтные значения; вместо этого создавайте их внутри тела функции (через `None` или sentinel) или возвращайте новую коллекцию.