Логотип YeaHub

База вопросов

Собеседования

Тренажёр

База ресурсов

Обучение

Навыки

Войти

Выбери, каким будет IT завтра — вместе c нами!

YeaHub — это полностью открытый проект, призванный объединить и улучшить IT-сферу. Наш исходный код доступен для просмотра на GitHub. Дизайн проекта также открыт для ознакомления в Figma.

© 2026 YeaHub

Документы

Медиа

Назад
Вопрос про Python: python, mutable default arguments, function arguments, side effects

Какое поведение возникает при использовании mutable default аргументов между вызовами функции?

Вопрос проверяет понимание особенностей работы с изменяемыми (mutable) объектами в качестве значений по умолчанию для аргументов функций в Python.

Короткий ответ

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

Длинный ответ

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

Почему это происходит?

Определение функции с аргументом по умолчанию def func(arg=[]) связывает имя arg с конкретным объектом списка, созданным в момент определения. Этот объект становится атрибутом функции (func.__defaults__). При каждом вызове, если аргумент не передан, используется ссылка на этот один и тот же объект из __defaults__. Любые модификации этого объекта (append, extend) изменяют его состояние, которое сохраняется для будущих вызовов.

Пример проблемного кода

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

print(add_item(1))  # Вывод: [1]
print(add_item(2))  # Вывод: [1, 2] — сюрприз!
print(add_item(3))  # Вывод: [1, 2, 3]

Как видно, список items не создаётся заново при каждом вызове. Вместо этого все добавленные элементы накапливаются в одном и том же списке.

Как избежать проблемы

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

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

print(add_item_safe(1))  # [1]
print(add_item_safe(2))  # [2] — теперь каждый вызов получает новый список

Этот подход гарантирует, что каждый вызов функции без аргумента items начинает работу с нового, пустого списка.

Где это применяется и почему важно

Понимание этого поведения критично для написания надёжного, предсказуемого кода, особенно в библиотеках и фреймворках, где функции могут вызываться многократно с разными контекстами. Это классический пример скрытого разделения состояния (shared state), который может приводить к трудноотлавливаемым багам в многопоточных приложениях или долгоживущих процессах (например, в веб-серверах).

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

Уровень

  • Рейтинг:

    4

  • Сложность:

    3

Навыки

  • Python

    Python

Ключевые слова

#python

#mutable default arguments

#function arguments

#side effects

Подпишись на Python Developer в телеграм