В языке Python показан фрагмент: def add_to_list(x, l=[]): l.append(x); return l — объясните неожиданное поведение и способы его устранения, обсудите различия в семантике между изменяемыми и неизменяемыми объектами

6 Фев в 15:34
18 +1
0
Ответы
1
Коротко — причина в том, что значение аргумента по умолчанию вычисляется один раз при определении функции и если это изменяемый объект (например список), то все вызовы будут использовать один и тот же объект.
Пример неожиданного поведения:
- функция:
def add_to_list(x, l=[]):
l.append(x)
return l
- вызовы:
add_to_list(1) → [1][1][1]
add_to_list(2) → [1,2][1, 2][1,2]
add_to_list(3, []) → [3][3][3] (здесь передан новый список)
Почему так: при определении функции создаётся один объект списка (по умолчанию) и каждый последующий вызов без явного аргумента использует тот же самый объект, поэтому изменения (append) аккумулируются.
Способы устранения:
1. Использовать sentinel `None` и создавать новый список внутри:
def add_to_list(x, l=None):
if l is None:
l = []
l.append(x)
return l
2. Создавать новый объект прямо в сигнатуре (если подходит), например использовать кортежы как неизменяемый контейнер, или явно копировать:
def f(l=[]):
l = l[:] # копия, затем менять
3. Использовать фабрики/декораторы / dataclass default_factory (в контексте классов) — подходящие средства создают новое значение при каждом вызове/инициализации.
Различия семантики изменяемых и неизменяемых объектов (кратко):
- Изменяемые объекты (list, dict, set и т.д.):
- Операции "внутри" объекта (например, `append`, `pop`, присвоение по индексу) изменяют сам объект. Если несколько имён ссылаются на один объект, они «видят» эти изменения.
- Передача в функцию передаёт ссылку на объект; функция может изменить объект, и изменения будут видны снаружи.
- Неизменяемые объекты (int, float, str, tuple и т.д.):
- Операции создают новый объект; исходный остаётся неизменным.
- В функции любые присвоения имени (например `x += 1` для `int`) создают новый объект и не влияют на внешний объект.
Короткий пример различий:
- int:
a = 111 def inc(x):
x += 111 # создаёт новый int, внешний a не меняется
- list:
b = []
def add_one(lst):
lst.append(111) # меняет объект b, внешний b видит изменение
Итого: избегайте изменяемых объектов как значений по умолчанию; применяйте паттерн с `None` или всегда создавайте/копируйте нужный объект внутри функции.
6 Фев в 15:44
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир