Почему в функциях Python не следует использовать изменяемые типы данных в качестве значений по умолчанию?

«Почему в функциях Python не следует использовать изменяемые типы данных в качестве значений по умолчанию?» — вопрос из категории Python, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Пример проблемы:

def add_item(item, items=[]):
    items.append(item)
    return items

print(add_item(1))  # Вывод: [1]
print(add_item(2))  # Вывод: [1, 2] - Ожидалось: [2], но список `items` сохранил предыдущее состояние!
print(add_item(3))  # Вывод: [1, 2, 3]

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

def add_item_correct(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

print(add_item_correct(1))  # Вывод: [1]
print(add_item_correct(2))  # Вывод: [2] - Каждый вызов получает новый список
print(add_item_correct(3, [10, 20])) # Вывод: [10, 20, 3] - Можно передать свой список

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