Ответ
Использование изменяемых типов данных (таких как списки, словари, множества) в качестве значений по умолчанию для аргументов функций в 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] - Можно передать свой список
Этот подход гарантирует, что каждый вызов функции, не передающий явно изменяемый аргумент, будет работать с новым, независимым экземпляром изменяемого объекта, предотвращая побочные эффекты.
Ответ 18+ 🔞
Да ты посмотри, какая подстава-то, ёпта! Вот сидишь ты, пишешь функцию, ни о чём не подозреваешь, а она тебе в один прекрасный день — раз! — и выстреливает в ногу, да так, что мало не покажется.
Смотри сюда, вот классика жанра, пиздец как не надо делать:
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]
Ну и что это за хуйня? Ты думал, что каждый раз, когда ты не передаёшь items, создаётся новый пустой список? А вот нихуя! Этот самый список [] создаётся один раз и навсегда в момент, когда интерпретатор читает твоё объявление функции. И потом этот же самый, блядь, объект таскается между всеми вызовами, как последняя шлюха. Добавил в него раз — он запомнил, добавил два — он всё равно помнит. В итоге получается не функция, а какая-то хитрая жопа с памятью, которая тебя обязательно подведёт в самый неподходящий момент.
А всё почему? Потому что изменяемые типы — списки, словари, множества — они как живые, сука. Их состояние меняется. И если ты используешь их как значение по умолчанию, ты по сути даёшь всем вызовам функции один и тот же живой объект. Это как если бы у тебя был один общий унитаз на весь офис, и никто бы его не смывал. Представил? Вот то-то же.
Так как же делать-то правильно, спросишь ты? А вот так, мой друг, вот так:
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] - Можно передать свой список
Видишь фокус? Мы ставим по умолчанию None — это неизменяемый и безопасный объект. А уже внутри функции проверяем: если нам прилетел None, значит, пользователь ничего не передавал, и нам нужно создать новый, свежий, девственно чистый список. И каждый вызов получит свой собственный, ни от кого не зависящий список. И можно спать спокойно.
Запомни эту простую мантру, и волнение ебать тебя оставит: изменяемые объекты в аргументах по умолчанию — это прямой путь в ад отладки. Всегда используй None и создавай объект внутри. И будет тебе счастье, а не неожиданный пиздец в продакшене.