Почему нельзя использовать изменяемый объект как значение аргумента по умолчанию?
- Что не так с def f(x=[])?
- Почему список в аргументе по умолчанию накапливает значения?
Значение по умолчанию вычисляется один раз — при определении функции, а не при каждом вызове.
- Изменяемый объект (
[],{}) тогда общий для всех вызовов и копит изменения - Правильно:
def f(items=None)и внутриif items is None: items = []
А ещё Иногда этим пользуются намеренно — как простым кэшем, но это лучше явно оформить
Подробно
Когда Python выполняет строку def, он создаёт объект функции и в этот же момент вычисляет значения по умолчанию. Они сохраняются в атрибуте __defaults__ и переиспользуются при каждом вызове, где аргумент не передан.
Для неизменяемых значений (0, None, строка) это незаметно. Но если по умолчанию стоит список или словарь и функция его меняет, изменения остаются в том же объекте и видны в следующих вызовах.
Стандартное решение — сентинел None: в сигнатуре None, а новый список создаётся внутри функции при каждом вызове. Если None — допустимое значение аргумента, используют собственный сентинел: _missing = object().
Та же логика касается любых выражений по умолчанию: def log(t=time.time()) запомнит время определения функции, а не вызова.
Пример
def add(item, items=[]):
items.append(item)
return items
add(1) # [1]
add(2) # [1, 2] — тот же список!
def add(item, items=None):
if items is None:
items = []
items.append(item)
return itemsЧто хотят услышать
- Причина: значение вычисляется один раз при
def. - Решение через
Noneи создание объекта внутри. - Понимание, что проблема только при изменении объекта.
Частые ошибки
- Объяснять «Python так кэширует списки» без указания момента вычисления.
- Решение
items = items or []: оно подменит и переданный пустой список, что может быть неожиданно для вызывающего.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✓Значение вычисляется один раз при определении функции и общее для всех вызовов
- ✗Значение по умолчанию вычисляется заново при каждом вызове функции
- ✗Python запрещает изменяемые значения по умолчанию
- ✗Проблема возникает только в многопоточном коде
Что спросят дальше
Разбор темы
- Функции — аргументы, области видимости, замыкания и декораторы
Как передаются аргументы, *args и **kwargs, LEGB, ловушка изменяемого значения по умолчанию, замыкания и позднее связывание, декораторы и functools.
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.