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

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

@python_quiz

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

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

Исходный код

python

Что происходит и почему

  • В Python значения аргументов по умолчанию вычисляются один раз — в момент определения функции, а не при каждом вызове.
  • Если значением по умолчанию является изменяемый объект (например, список, словарь, объект), то все последующие вызовы функции без явного аргумента будут использовать один и тот же объект.
  • В примере lst создаётся один раз как пустой список при определении функции. Первый вызов добавляет 1 в этот список — он становится [1]. Второй вызов добавляет 2 в тот же список — он становится [1, 2].

Пример выполнения показывает именно это:

python

Анализ возможных вариантов вывода

Рассмотрим типичные варианты, которые могут прийти в голову:

  • Выводы по одному элементу в каждой строке, но с разными значениями:

- [1] и затем [2] — нет, второй вызов не создаёт новый список, он использует тот же.
- [1] и затем [1] — нет, второй вызов добавляет новый элемент в список, поэтому результат изменится.

  • Ошибка времени выполнения — нет, код корректен и ошибок не вызывает.
  • Накопление элементов: [1] и затем [1, 2] — это именно то, что и происходит, потому что список по умолчанию сохраняется между вызовами.

Как избежать сюрпризов

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

python

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

Альтернативный вариант — явно передавать новый список при каждом вызове, когда это уместно:

python

(Здесь используется предыдущая безопасная реализация с lst=None.)

Выводы и рекомендации

  • Не используйте изменяемые объекты (списки, словари, множества) непосредственно в качестве значений аргументов по умолчанию, если вы ожидаете, что при каждом вызове будет создаваться новый объект.
  • Используйте None и создавайте новый объект внутри функции, или явно передавайте объект при вызове.
  • Понимание того, что значения по умолчанию вычисляются один раз при определении функции, помогает избежать трудноуловимых багов в практике.

Report Page