Рассмотрите Python-функцию: def add_item(item, lst=[]): lst.append(item); return lst Объясните, почему при последовательных вызовах результат может быть неожиданным, как это связано с областью видимости и временем создания объектов, и как исправить
Проблема: при последовательных вызовах без аргумента `lst` все вызовы используют одну и ту же список-значение по умолчанию, поэтому элементы накапливаются. Почему так происходит (коротко и технически): - Значение параметров по умолчанию вычисляется один раз — в момент определения функции, а не при каждом вызове. В результате для `def add_item(item, lst=[])` создаётся один объект списка и сохраняется в `function.__defaults__`. - При каждом вызове без явного `lst` функция получает ссылку на этот один и тот же список (те же id), и `append` модифицирует его. Пример нежелательного поведения: ``` def add_item(item, lst=[]): lst.append(item) return lst print(add_item(1)) # [1] print(add_item(2)) # [1, 2] <-- ожидают [2], но список общий ``` Как исправить (рекомендуемый вариант — sentinel 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] ``` Альтернативы: - Всегда передавать новый список при вызове: `add_item(1, [])`. - Использовать неизменяемое значение по умолчанию (например, `None`/`()`), или фабрику для создания нового объекта внутри функции. - В редких случаях, если нужно кэширование между вызовами, можно намеренно использовать мутируемый default, но это следует документировать явно.
Почему так происходит (коротко и технически):
- Значение параметров по умолчанию вычисляется один раз — в момент определения функции, а не при каждом вызове. В результате для `def add_item(item, lst=[])` создаётся один объект списка и сохраняется в `function.__defaults__`.
- При каждом вызове без явного `lst` функция получает ссылку на этот один и тот же список (те же id), и `append` модифицирует его.
Пример нежелательного поведения:
```
def add_item(item, lst=[]):
lst.append(item)
return lst
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] <-- ожидают [2], но список общий
```
Как исправить (рекомендуемый вариант — sentinel 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]
```
Альтернативы:
- Всегда передавать новый список при вызове: `add_item(1, [])`.
- Использовать неизменяемое значение по умолчанию (например, `None`/`()`), или фабрику для создания нового объекта внутри функции.
- В редких случаях, если нужно кэширование между вызовами, можно намеренно использовать мутируемый default, но это следует документировать явно.