Дан код на Python: def append_to_list(x, lst=[]): lst.append(x); return lst. Объясните поведение при последовательных вызовах, почему возникает «ловушка», и предложите корректные альтернативы
Поведение и причина «ловушки» Даная функция def append_to_list(x, lst=[]): lst.append(x) return lst по умолчанию создает список один раз при определении функции, а не при каждом вызове. Поэтому при последовательных вызовах без явного второго аргумента используется один и тот же список, и элементы накапливаются: print(append_to_list(111)) # => [1][1][1] print(append_to_list(222)) # => [1,2][1, 2][1,2] print(append_to_list(333, lst=[])) # => [3][3][3] (при передаче нового списка поведение обычное) Итого: «ловушка» в том, что дефолтный mutable-объект (список, словарь и т.п.) создаётся один раз и затем разделяется между вызовами. Корректные альтернативы 1) Использовать None и создавать новый список внутри: def append_to_list(x, lst=None): if lst is None: lst = [] lst.append(x) return lst 2) Использовать уникальный sentinel: _sentinel = object() def append_to_list(x, lst=_sentinel): if lst is _sentinel: lst = [] lst.append(x) return lst 3) Возвращать новый список (не мутировать входной): def append_to_list(x, lst=None): return (lst or []) + [x] Примечания - То же касается любых изменяемых типов (dict, set, пользовательские объекты). - Если нужен именно постоянный накапливающийся список между вызовами — делайте это явно (глобальная переменная или параметр класса) и документируйте.
Даная функция
def append_to_list(x, lst=[]):
lst.append(x)
return lst
по умолчанию создает список один раз при определении функции, а не при каждом вызове. Поэтому при последовательных вызовах без явного второго аргумента используется один и тот же список, и элементы накапливаются:
print(append_to_list(111)) # => [1][1][1]
print(append_to_list(222)) # => [1,2][1, 2][1,2]
print(append_to_list(333, lst=[])) # => [3][3][3] (при передаче нового списка поведение обычное)
Итого: «ловушка» в том, что дефолтный mutable-объект (список, словарь и т.п.) создаётся один раз и затем разделяется между вызовами.
Корректные альтернативы
1) Использовать None и создавать новый список внутри:
def append_to_list(x, lst=None):
if lst is None:
lst = []
lst.append(x)
return lst
2) Использовать уникальный sentinel:
_sentinel = object()
def append_to_list(x, lst=_sentinel):
if lst is _sentinel:
lst = []
lst.append(x)
return lst
3) Возвращать новый список (не мутировать входной):
def append_to_list(x, lst=None):
return (lst or []) + [x]
Примечания
- То же касается любых изменяемых типов (dict, set, пользовательские объекты).
- Если нужен именно постоянный накапливающийся список между вызовами — делайте это явно (глобальная переменная или параметр класса) и документируйте.