Изменяемый аргумент по умолчанию в Python

Изменяемый аргумент по умолчанию в Python

@python_quiz

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

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

Условие (код)

python

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

Если выполнить этот код, вывод будет:

Почему оба вывода показывают [1, 1], а не, скажем, [1] и [1, 1]? Разберём пошагово.

Пошаговое объяснение

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

Важно: аргумент по умолчанию не пересоздаётся при каждом вызове — это ключ к пониманию поведения.

Анализ вариантов ответов (пояснения)

  • Выведет: [1] [1]

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

  • Выведет: [1] [1, 1]

Интуитивно кажется логичным: первый вызов дал [1], второй — [1,1]. Однако оба результата ссылаются на один и тот же объект, и когда второй вызов модифицирует список, это отражается и на первом результате при финальном выводе.

  • Выведет: [1, 1] [1, 1]

Соответствует реальному поведению: один и тот же изменяемый объект используется повторно, и оба вывода показывают финальное состояние списка.

  • Выбросит исключение

Исключения не происходит: операции с списком и вызовы выполняются корректно.

Как правильно писать, чтобы избежать сюрпризов

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

python

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

Практические замечания и рекомендации

  • Такой паттерн — частая ловушка для новичков. Он встречается при использовании списков, словарей и других изменяемых типов.
  • Иногда сохранение состояния между вызовами полезно (кеширование, накапливание), но если это не намеренное поведение — лучше явно создавать новый объект внутри функции.
  • Для неизменяемых типов (int, str, tuple) подобной проблемы нет, потому что их нельзя изменять.

Заключение: изменяемые значения по умолчанию создаются один раз при определении функции. Если не хотите разделять объект между вызовами — используйте None и создавайте новый объект внутри функции.

Report Page