Рассмотрите следующий фрагмент на Python: "def f(x=[]): x.append(1); return x" — объясните, почему поведение функции может быть неожиданным, как его исправить и когда использование изменяемых значений по умолчанию допустимо
Коротко: поведение неожиданно, потому что значение параметра по умолчанию вычисляется один раз при определении функции и потом переиспользуется — поэтому один и тот же изменяемый объект (список) накапливает значения между вызовами. Пример (показательно): def f(x=[]): x.append(111) return x Вызовы: f() -> [1][1][1]
f() -> [1,1][1, 1][1,1]
f() -> [1,1,1][1, 1, 1][1,1,1] Исправление — использовать немутируемый маркер (обычно None) и создавать новый список внутри тела: def f(x=None): if x is None: x = [] x.append(111) return x Теперь: f() -> [1][1][1]
f() -> [1][1][1]
f() -> [1][1][1] Когда допустимо использовать изменяемые значения по умолчанию: - Когда вы намеренно хотите сохранять состояние между вызовами (например, кэш, аккумулятор). Тогда поведение — ожидаемое и полезное. - Когда значение по умолчанию само по себе немутируемо (числа, строки, кортежи) — таких проблем нет. Замечания: если вам нужен глобальный/разделяемый кэш, лучше сделать это явно (атрибут функции, класс, functools.lru_cache и т.д.), чтобы код был читабельнее и безопаснее (особенно в многопоточной среде).
Пример (показательно):
def f(x=[]):
x.append(111)
return x
Вызовы:
f() -> [1][1][1] f() -> [1,1][1, 1][1,1] f() -> [1,1,1][1, 1, 1][1,1,1]
Исправление — использовать немутируемый маркер (обычно None) и создавать новый список внутри тела:
def f(x=None):
if x is None:
x = []
x.append(111)
return x
Теперь:
f() -> [1][1][1] f() -> [1][1][1] f() -> [1][1][1]
Когда допустимо использовать изменяемые значения по умолчанию:
- Когда вы намеренно хотите сохранять состояние между вызовами (например, кэш, аккумулятор). Тогда поведение — ожидаемое и полезное.
- Когда значение по умолчанию само по себе немутируемо (числа, строки, кортежи) — таких проблем нет.
Замечания: если вам нужен глобальный/разделяемый кэш, лучше сделать это явно (атрибут функции, класс, functools.lru_cache и т.д.), чтобы код был читабельнее и безопаснее (особенно в многопоточной среде).