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

8 Мая в 08:09
16 +1
0
Ответы
1
Проблема. По умолчанию аргументы функции вычисляются один раз — при определении функции. Для сигнатуры
def append_item(x, lst=[]):
мутируемый объект `[]` создаётся один раз и затем переиспользуется при каждом вызове. В результате последовательные вызовы накапливают элементы в одном и том же списке: например, вызовы
append_item(1) → [1] [1] [1],
append_item(2) → [1,2] [1, 2] [1,2],
append_item(3) → [1,2,3] [1, 2, 3] [1,2,3].
Это часто бывает неожиданно, если ожидалось, что по умолчанию будет свежий пустой список при каждом вызове.
Почему так происходит (кратко). По определению Python вычисляет значение дефолтного аргумента один раз (в момент определения функции), а не при каждом вызове; для неизменяемых типов это безопасно, для изменяемых (list, dict, set) — приводит к совместному состоянию.
Как исправить (варианты для разных сценариев)
1) Нужен новый пустой список при каждом вызове (стандартное решение)
def append_item(x, lst=None):
if lst is None:
lst = []
lst.append(x)
return lst
2) Нельзя менять список, нужно возвращать новый (функциональный стиль)
def append_item(x, lst=None):
if lst is None:
lst = []
return lst + [x] # не меняет исходный lst
3) Если аргумент может быть передан извне, но вы не хотите менять переданный список (копировать)
def append_item(x, lst=None):
if lst is None:
lst = []
else:
lst = list(lst) # shallow copy
lst.append(x)
return lst
4) Если намеренно нужно иметь один общий список между вызовами (кэш/накопитель) — тогда делайте это явно
_shared = []
def append_item(x, lst=_shared):
lst.append(x)
return lst
(но документируйте намерение, чтобы не было сюрпризов)
5) Альтернативы для неизменяемых дефолтов: использовать кортеж как дефолт и преобразовывать в список внутри, если нужно:
def append_item(x, lst=()):
lst = list(lst)
lst.append(x)
return lst
Краткое правило: не используйте изменяемые объекты как значения дефолтных аргументов, если не хотите совместного состояния между вызовами — вместо этого применяйте `None` (с создание объекта внутри) или делайте копию входных данных.
8 Мая в 08:18
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир