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