Мутабельные аргументы по умолчанию в Python

Мутабельные аргументы по умолчанию в Python

@python_quiz

Разберем этот квиз

Кратко: в Python аргументы по умолчанию вычисляются один раз при определении функции. Если значение — изменяемый объект (например, список), несколько вызовов будут использовать один и тот же объект.

Условие (исходный код)

python

Что выведет этот код и почему

При первом вызове функция добавляет 'Ann' в список по умолчанию и возвращает ['Ann'].
При втором вызове используется тот же самый список, поэтому в него добавляют 'Bob', и функция возвращает ['Ann', 'Bob'].

Фактически вывод будет:

python

Почему так происходит:

  • Значение по умолчанию для параметра users (пустой список) создаётся один раз — в момент определения функции.
  • При каждом вызове, если аргумент users не передан, используется один и тот же объект списка, и изменения (append) накапливаются.

Чтобы это продемонстрировать явно, можно посмотреть id объекта списка:

python

Вы увидите, что id(users) одинаковый для обоих вызовов.

Разбор вариантов ответов

  • "['Ann'] и ['Bob']" — неверно. Это было бы верно, если бы на каждом вызове создавался новый список по умолчанию, но в реальности список тот же, поэтому второй вывод содержит оба имени.
  • "['Ann'] и ['Ann', 'Bob']" — соответствует фактическому поведению кода: список общий, изменения накапливаются.
  • "['Ann', 'Bob'] и ['Bob']" — неверно: никакой из вызовов не вернёт сначала оба имени, а потом только 'Bob'.
  • "['Ann', 'Bob'] и ['Ann', 'Bob']" — неверно: первая печать произойдёт до добавления 'Bob', поэтому не может сразу вернуть оба имени.

Как правильно писать такую функцию (без побочных эффектов)

Если хочется, чтобы при каждом вызове по умолчанию создавался новый список (а не использовался один и тот же), применяют трюк с None:

python

Теперь каждый вызов без явного аргумента получает новый список.

Альтернативы и советы

  • Используйте None как безопасный маркер для значений по умолчанию, когда ожидается изменяемый объект.
  • Если нужно, чтобы состояние сохранялось между вызовами (намеренно), можно использовать изменяемый объект по умолчанию — но делайте это осознанно и документируйте поведение.
  • Для семантики «пустое значение» можно использовать уникальный sentinel-объект, если None — допустимый аргумент:
python
  • В типизированном коде можно явно аннотировать параметр: users: Optional[List[str]] = None.

Вывод

Мутабельные объекты в качестве значений по умолчанию — частая ловушка в Python. По умолчанию список создаётся один раз при определении функции, поэтому изменения будут сохраняться между вызовами. Чаще всего безопаснее использовать None и создавать новый объект внутри функции.

Report Page