Короткий ответ
Код выведет [1], а затем [1, 2]. Причина — изменяемый список как значение аргумента по умолчанию: такой объект создаётся один раз и разделяется между всеми вызовами функции.
Как это работает подробнее
Значения аргументов по умолчанию вычисляются в момент определения функции, а не при каждом вызове.
- Единственный экземпляр. Когда Python выполняет строку с
def, список[]создаётся один раз, и ссылка на него сохраняется как значение по умолчанию. - Разделение между вызовами. Первый вызов добавляет в общий список 1, второй обращается к тому же объекту и добавляет 2 — получается
[1, 2]. - Накопление состояния. Такой список ведёт себя как глобальное состояние, сохраняя значения между вызовами, — это классический источник трудноуловимых багов.
- Другие изменяемые типы. Та же проблема возникает со словарями и множествами, поэтому их тоже избегают в значениях по умолчанию.
Пример кода
Опасный и идиоматичный вариант
def add_item(item, some_list=[]):
some_list.append(item)
return some_list
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] — список общий для всех вызовов
def add_item(item, some_list=None):
if some_list is None:
some_list = []
some_list.append(item)
return some_list
print(add_item(1)) # [1]
print(add_item(2)) # [2] — каждый раз новый списокЧто использовать на практике
- None как маркер. Используй
Noneв значении по умолчанию и создавай новый список внутри функции — это идиоматический паттерн Python. - Проверка на None. Сравнение делай через
is None, чтобы понять, передан ли аргумент явно. - Явная передача. Если функция должна копить элементы в переданном списке, передавай его явно — тогда изменяемый дефолт вообще не нужен.
Подводные камни
- Общий дефолт. Забыв, что значение по умолчанию создаётся один раз, легко получить «магическое» накопление данных между вызовами.
- Непредсказуемость. Результат зависит от порядка и числа предыдущих вызовов, что делает поведение функции неочевидным и трудно тестируемым.
- Не только списки. Опасны любые изменяемые значения по умолчанию — словари и множества ведут себя точно так же.
Как отвечать на собеседовании
- Назови вывод: сначала
[1], затем[1, 2]→ объясни, что аргументы по умолчанию вычисляются один раз при определении функции → покажи, что список разделяется между вызовами → приведи идиоматичное решение сNoneи созданием нового списка внутри функции.