Приведён фрагмент на Python: def append_item(x, lst=[]): lst.append(x); return lst; Объясните поведение этой функции при последовательных вызовах и предложите корректные варианты реализации с объяснением отличий по сложности и побочным эффектам

16 Мар в 09:05
25 +1
0
Ответы
1
Коротко — почему так: аргумент по умолчанию вычисляется один раз при определении функции и если это изменяемый объект (как `[]`), то одна и та же список используется при всех вызовах. Поэтому элементы накапливаются между вызовами.
Пример поведения:
- вызов `append_item(111)` вернёт `[111]`
- затем `append_item(222)` вернёт `[111, 222]`
- затем `append_item(333)` вернёт `[111, 222, 333]`
Корректные варианты реализации и их отличия:
1) Идиоматичный вариант (если нужно, чтобы по умолчанию создавался новый список):
```
def append_item(x, lst=None):
if lst is None:
lst = []
lst.append(x)
return lst
```
- Сложность операции append: O(1)\mathcal{O}(1)O(1) амортизированно.
- Побочные эффекты: если вы передали существующий список, он будет изменён; при отсутствии аргумента новый список создаётся при каждом вызове — нет «накопления» между вызовами.
2) Без мутации входного списка (возвращает новый список):
```
def append_item(x, lst=None):
if lst is None:
lst = []
return lst + [x]
```
- Сложность: O(n)\mathcal{O}(n)O(n) по длине `lst` (создаётся копия).
- Побочные эффекты: входной `lst` не изменяется. Подходит, если нужно иммутабельное поведение.
3) Явная копия и append (аналогично предыдущему, но явно):
```
def append_item(x, lst=None):
if lst is None:
lst = []
new = lst.copy()
new.append(x)
return new
```
- Сложность: O(n)\mathcal{O}(n)O(n).
- Побочные эффекты: отсутствуют для входного списка.
4) Использование маркера вместо `None` (если `None` — допустимое значение для `lst`):
```
_marker = object()
def append_item(x, lst=_marker):
if lst is _marker:
lst = []
lst.append(x)
return lst
```
- Поведение и сложность как в варианте (1), но безопасно, если пользователь может явно передавать `None`.
Дополнение: исходное поведение иногда используется намеренно для накопления состояния между вызовами (например, кэш). Если это не желаемо, используйте вариант (1) или (4). Если важно не изменять переданный список — используйте (2) или (3).
16 Мар в 09:11
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир