Разберите код на Python: def add_items(lst=[]): for i in range(3): lst.append(i) return lst — объясните проблему с дефолтным изменяемым аргументом, приведите исправление и обсудите, почему это распространённая ошибка у новичков.
Код: def add_items(lst=[]): for i in range(3): lst.append(i) return lst Проблема - Значение дефолтного аргумента вычисляется один раз при определении функции, а не при каждом вызове. Поскольку `lst` — изменяемый объект (список), все вызовы без явного аргумента будут использовать один и тот же список. В результате последовательные вызовы накапливают элементы. - Пример поведения: - первый вызов: `add_items()` возвращает [0,1,2] [0,1,2] [0,1,2]; - второй вызов: `add_items()` возвращает [0,1,2,0,1,2] [0,1,2,0,1,2] [0,1,2,0,1,2] — тот же список расширился ещё на 333 элемента. Исправление (использовать `None` как сигнал и создавать новый список внутри функции): def add_items(lst=None): if lst is None: lst = [] for i in range(3): lst.append(i) return lst Альтернативы: присваивание через `lst = [] if lst is None else lst` или использование спискового включения/функций, но предпочтителен паттерн с `None`, чтобы отличать "аргумент не передан" от "передан пустой список". Почему это частая ошибка у новичков - Ожидание, что дефолтное выражение будет вычисляться при каждом вызове (интуитивно). На самом деле оно вычисляется единожды. - Неочевидность разницы между неизменяемыми (например, числа, кортежи, строки) и изменяемыми типами (списки, словари), поэтому при использовании неизменяемого дефолта проблема не видна. - Часто примеры в учебниках используют `[]` как удобный дефолт, и новичок копирует шаблон, не понимая побочного эффекта. Коротко: не используйте изменяемые объекты как значения по умолчанию; вместо этого используйте `None` и создавайте объект внутри функции.
def add_items(lst=[]):
for i in range(3):
lst.append(i)
return lst
Проблема
- Значение дефолтного аргумента вычисляется один раз при определении функции, а не при каждом вызове. Поскольку `lst` — изменяемый объект (список), все вызовы без явного аргумента будут использовать один и тот же список. В результате последовательные вызовы накапливают элементы.
- Пример поведения:
- первый вызов: `add_items()` возвращает [0,1,2] [0,1,2] [0,1,2];
- второй вызов: `add_items()` возвращает [0,1,2,0,1,2] [0,1,2,0,1,2] [0,1,2,0,1,2] — тот же список расширился ещё на 333 элемента.
Исправление (использовать `None` как сигнал и создавать новый список внутри функции):
def add_items(lst=None):
if lst is None:
lst = []
for i in range(3):
lst.append(i)
return lst
Альтернативы: присваивание через `lst = [] if lst is None else lst` или использование спискового включения/функций, но предпочтителен паттерн с `None`, чтобы отличать "аргумент не передан" от "передан пустой список".
Почему это частая ошибка у новичков
- Ожидание, что дефолтное выражение будет вычисляться при каждом вызове (интуитивно). На самом деле оно вычисляется единожды.
- Неочевидность разницы между неизменяемыми (например, числа, кортежи, строки) и изменяемыми типами (списки, словари), поэтому при использовании неизменяемого дефолта проблема не видна.
- Часто примеры в учебниках используют `[]` как удобный дефолт, и новичок копирует шаблон, не понимая побочного эффекта.
Коротко: не используйте изменяемые объекты как значения по умолчанию; вместо этого используйте `None` и создавайте объект внутри функции.