В коде на Python def append_item(x, lst=[]): lst.append(x); return lst; объясните, почему вызовы append_item(1), append_item(2) дают неожиданные результаты, опишите природу ошибки и предложите корректные варианты реализации и тесты
Проблема: в Python значение аргумента по умолчанию вычисляется один раз при определении функции и переиспользуется при последующих вызовах. Если это изменяемый объект (например, список), все вызовы будут работать с одной и той же общей структурой. Пример симптома - Определение: `def append_item(x, lst=[]): lst.append(x); return lst` - Вызовы `append_item(1)` затем `append_item(2)` дадут не ожидаемые независимые списки, а накопление: первый вызов вернёт [1][1][1], второй — [1,2][1, 2][1,2]. Природа ошибки - Это не «баг» интерпретатора, а семантика: лист `lst=[]` создаётся при загрузке тела функции и живёт между вызовами. Если вы изменяете этот объект внутри функции, изменения видны в последующих вызовах — получается скрытое состояние. Корректные варианты реализации 1) Стандартный паттерн с `None`: ```python def append_item(x, lst=None): if lst is None: lst = [] lst.append(x) return lst ``` 2) С проверкой уникального маркера (если `None` может быть допустимым значением): ```python _sentinel = object() def append_item(x, lst=_sentinel): if lst is _sentinel: lst = [] lst.append(x) return lst ``` 3) Функциональный подход — не изменять входной список, возвращать новый: ```python def append_item(x, lst=None): if lst is None: lst = [] return lst + [x] # создаёт новый список ``` Простые тесты (assert): ```python # Тест для проблемного варианта (демонстрация неправильного поведения) def buggy(): def append_item(x, lst=[]): lst.append(x) return lst a = append_item(1) b = append_item(2) return a, b assert buggy()[0] == [1] assert buggy()[1] == [1, 2] # неожиданно: второй список содержит элемент из первого # Тесты для корректного варианта (ожидаемое поведение) def correct(): def append_item(x, lst=None): if lst is None: lst = [] lst.append(x) return lst a = append_item(1) b = append_item(2) return a, b assert correct()[0] == [1] assert correct()[1] == [2] # правильно: состояния не смешиваются ``` Краткое резюме: не используйте изменяемые объекты как значения по умолчанию; применяйте `None` (или маркер) и создавайте новую структуру внутри функции, либо возвращайте новый объект вместо модификации входного.
Пример симптома
- Определение: `def append_item(x, lst=[]): lst.append(x); return lst`
- Вызовы `append_item(1)` затем `append_item(2)` дадут не ожидаемые независимые списки, а накопление: первый вызов вернёт [1][1][1], второй — [1,2][1, 2][1,2].
Природа ошибки
- Это не «баг» интерпретатора, а семантика: лист `lst=[]` создаётся при загрузке тела функции и живёт между вызовами. Если вы изменяете этот объект внутри функции, изменения видны в последующих вызовах — получается скрытое состояние.
Корректные варианты реализации
1) Стандартный паттерн с `None`:
```python
def append_item(x, lst=None):
if lst is None:
lst = []
lst.append(x)
return lst
```
2) С проверкой уникального маркера (если `None` может быть допустимым значением):
```python
_sentinel = object()
def append_item(x, lst=_sentinel):
if lst is _sentinel:
lst = []
lst.append(x)
return lst
```
3) Функциональный подход — не изменять входной список, возвращать новый:
```python
def append_item(x, lst=None):
if lst is None:
lst = []
return lst + [x] # создаёт новый список
```
Простые тесты (assert):
```python
# Тест для проблемного варианта (демонстрация неправильного поведения)
def buggy():
def append_item(x, lst=[]):
lst.append(x)
return lst
a = append_item(1)
b = append_item(2)
return a, b
assert buggy()[0] == [1]
assert buggy()[1] == [1, 2] # неожиданно: второй список содержит элемент из первого
# Тесты для корректного варианта (ожидаемое поведение)
def correct():
def append_item(x, lst=None):
if lst is None:
lst = []
lst.append(x)
return lst
a = append_item(1)
b = append_item(2)
return a, b
assert correct()[0] == [1]
assert correct()[1] == [2] # правильно: состояния не смешиваются
```
Краткое резюме: не используйте изменяемые объекты как значения по умолчанию; применяйте `None` (или маркер) и создавайте новую структуру внутри функции, либо возвращайте новый объект вместо модификации входного.