Ниже фрагмент Python: def foo(x=[]): x.append(1); return x; объясните неожиданное поведение при многократных вызовах foo() и предложите корректные варианты реализации
Причина: значение аргумента по умолчанию вычисляется один раз при определении функции и затем переиспользуется. В вашем случае один и тот же список используется при всех вызовах, поэтому элементы накапливаются: первый вызов даёт [1][1][1], второй — [1,1][1,1][1,1], третий — [1,1,1][1,1,1][1,1,1]. Корректные варианты 1) Создавать список внутри при каждом вызове (обычный паттерн с None): def foo(x=None): if x is None: x = [] x.append(1) return x 2) Не мутировать переданный список — возвращать новый список: def foo(x=None): if x is None: return [1] return x + [1] 3) Если нужен неизменяемый дефолт или копирование аргумента: def foo(x=()): return list(x) + [1] или def foo(x=None): x = [] if x is None else list(x) # копируем, чтобы не менять исходный x.append(1) return x Выбирайте (1), если ожидается изменение и хранение состояния в возвращаемом списке; (2) или версия с копированием, если нежелательно менять переданный аргумент.
Корректные варианты
1) Создавать список внутри при каждом вызове (обычный паттерн с None):
def foo(x=None):
if x is None:
x = []
x.append(1)
return x
2) Не мутировать переданный список — возвращать новый список:
def foo(x=None):
if x is None:
return [1]
return x + [1]
3) Если нужен неизменяемый дефолт или копирование аргумента:
def foo(x=()):
return list(x) + [1]
или
def foo(x=None):
x = [] if x is None else list(x) # копируем, чтобы не менять исходный
x.append(1)
return x
Выбирайте (1), если ожидается изменение и хранение состояния в возвращаемом списке; (2) или версия с копированием, если нежелательно менять переданный аргумент.