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

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

@python_quiz

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

Условие (код из квиза):

python

Что выведет эта программа:

text

Пошаговый разбор

  • При определении функции параметр buffer получает значение по умолчанию — и этот объект (список) создаётся ровно один раз в момент определения функции, а не при каждом вызове.
  • Первый вызов collect(1) использует этот общий список по умолчанию: в него добавляется 1 → список становится [1], и печатается [1].
  • Второй вызов collect(2, []) получает явно переданный новый список [], в который добавляется 2 → печатается [2]. Этот вызов не затрагивает список по умолчанию.
  • Третий вызов collect(3) снова использует тот самый список по умолчанию, который уже содержит [1] после первого вызова; добавляется 3 → список становится [1, 3], и печатается [1, 3].

Почему это важно

Мутируемые объекты (например, list, dict, set) в качестве значений по умолчанию — частая ловушка для начинающих в Python. Ожидание, что значение по умолчанию каждый раз будет «свежим», неверно: объект создаётся один раз и разделяется между вызовами, которые не передают этот аргумент явно.

Разбор вариантов ответов (из квиза)

  • Вариант, где выводы идут как

`
[1]
[2]
[3]
`
— ошибочен, потому что третий вызов использует тот же буфер, что и первый, а значит в нём уже есть 1.

  • Вариант

`
[1]
[2]
[1, 3]
`
— совпадает с реальным поведением программы (см. объяснение выше).

  • Вариант

`
[1]
[2]
[3, 1]
`
— неверен, порядок элементов в списке по умолчанию будет сохраняться в порядке добавления: сначала 1, потом 3 → [1, 3], а не [3, 1].

  • Вариант

`
[1]
[2]
[1, 2, 3]
`
— тоже неверен: второй вызов использовал отдельный список, поэтому 2 не попал в список по умолчанию.

Как правильно избежать побочных эффектов

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

python

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

Короткое резюме

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

Report Page