Кейс с кодом на Python: функция использует mutable default аргумент def add(x, lst=[]): lst.append(x); return lst — объясните нежелательное поведение, почему оно возникает и как это исправить
Проблема: в определении def add(x, lst=[]): lst.append(x) return lst значение аргумента по умолчанию lst=[]lst=[]lst=[] создаётся один раз при определении функции и затем используется повторно при всех вызовах. Из‑за этого список накапливает элементы между вызовами: Пример (поведение функции выше): add(1) -> [1] [1] [1] add(2) -> [1,2] [1, 2] [1,2] (а не ожидаемое [2] [2] [2]) Почему так происходит: объекты‑по‑умолчанию вычисляются один раз (когда модуль загружается), а не при каждом вызове. Для изменяемых объектов (list, dict, set и т.п.) это приводит к разделяемому состоянию. Как исправить (стандартный паттерн — использовать None как маркер и создавать новый список внутри функции): def add(x, lst=None): if lst is None: lst = [] lst.append(x) return lst Теперь: add(1) -> [1] [1] [1] add(2) -> [2] [2] [2] Альтернативы/заметки: - Если вам действительно нужно хранить накопленное состояние между вызовами, то текущее поведение годится (но документируйте намерение). - Для не‑мутабельных типов (например, кортежи) такой проблемы нет.
def add(x, lst=[]):
lst.append(x)
return lst
значение аргумента по умолчанию lst=[]lst=[]lst=[] создаётся один раз при определении функции и затем используется повторно при всех вызовах. Из‑за этого список накапливает элементы между вызовами:
Пример (поведение функции выше):
add(1) -> [1] [1] [1]
add(2) -> [1,2] [1, 2] [1,2] (а не ожидаемое [2] [2] [2])
Почему так происходит: объекты‑по‑умолчанию вычисляются один раз (когда модуль загружается), а не при каждом вызове. Для изменяемых объектов (list, dict, set и т.п.) это приводит к разделяемому состоянию.
Как исправить (стандартный паттерн — использовать None как маркер и создавать новый список внутри функции):
def add(x, lst=None):
if lst is None:
lst = []
lst.append(x)
return lst
Теперь:
add(1) -> [1] [1] [1]
add(2) -> [2] [2] [2]
Альтернативы/заметки:
- Если вам действительно нужно хранить накопленное состояние между вызовами, то текущее поведение годится (но документируйте намерение).
- Для не‑мутабельных типов (например, кортежи) такой проблемы нет.