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

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

@python_quiz

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

Код задачи — очень частая ловушка для тех, кто начинает работать с Python:

python

Разберём, почему этот фрагмент ведёт себя именно так и как получить ожидаемое поведение.

Что происходит — пошагово

  1. Значение аргументов по умолчанию вычисляется один раз — при определении функции. В данном случае список [] создаётся единожды и затем переиспользуется при каждом вызове f() без явного аргумента.
  2. Вызов f() делает a.append(1) — то есть изменяет (мутабельно) тот же объект-список.
  3. В выражении print(f(), f()) Python вычисляет аргументы функции print слева направо: сначала выполняется первый f(), затем второй f(). Но важный момент — оба вызова оперируют одним и тем же списком.
  4. После второго вызова список содержит два элемента. Поскольку оба возвращённых значения ссылаются на один и тот же объект, при печати вы увидите обновлённое состояние этого объекта дважды.

Итоговый вывод в консоль будет:

Демонстрация с id(), чтобы увидеть, что это один и тот же объект

python

Примерный вывод покажет одинаковые id для r1 и r2, а также итоговое содержимое списка — [1, 1] в обоих местах.

Почему некоторые ожидают другой результат

  • Ожидание вида [1] [1] исходит из предположения, что каждый вызов создаёт новую пустую списковую структуру. Но это неверно для аргументов по умолчанию, когда они мутабельны.
  • Ожидание вида [1] [1, 1] встречается чаще: люди думают, что первый f() вернул список [1] и что это "слепок", который не изменится после второго вызова. На самом деле возвращается ссылка на один и тот же объект, и последующие мутации видны во всех ссылках на этот объект.

Как избежать такой неожиданности

Правильный и идиоматичный способ — использовать значение по умолчанию None и создавать новый список внутри функции:

python

Альтернативы (в зависимости от задачи):

  • Возвращать копию списка, если нужно вернуть «снимок» состояния в момент вызова:
python
  • Явно передавать новый список при каждом вызове.

Вывод

Мутабельные объекты в качестве значений аргументов по умолчанию ведут себя как один общий объект для всех вызовов функции. Это удобно в редких случаях, но чаще становится источником багов. Используйте None + инициализацию внутри функции или возвращайте копии, если хотите избежать побочных эффектов.

Report Page