Изменение элемента во вложенном списке без мутации исходных данных

4. Изменение элемента во вложенном списке без мутации исходных данных

Условие задачи:
Дан словарь, в одном из ключей которого хранится список. Нужно изменить один элемент этого списка так, чтобы исходный словарь и исходный список остались без изменений.
Требуется получить новый словарь с обновленным значением, не мутируя исходные структуры данных.

Спойлеры к решению
Подсказки
  • Нельзя менять список напрямую через data["key"][index] = value, потому что это изменит исходный список.
  • Обычная копия словаря через data.copy() копирует только сам словарь, но вложенный список останется тем же объектом.
  • Нужно создать новый словарь.
  • Для изменяемого списка нужно создать отдельную копию.
  • После этого в новом словаре заменить старый список на обновлённый.
Решение
def update_list_item(data: dict, key: str, index: int, new_value) -> dict:
    new_list = data[key].copy()
    new_list[index] = new_value

    new_data = data.copy()
    new_data[key] = new_list

    return new_data

Пример использования:

numbers = [1, 2, 3]

data = {
    "name": "test",
    "numbers": numbers
}

new_data = update_list_item(data, "numbers", 1, 99)

print(data)      # {'name': 'test', 'numbers': [1, 2, 3]}
print(numbers)   # [1, 2, 3]
print(new_data)  # {'name': 'test', 'numbers': [1, 99, 3]}

Функция не мутирует исходный словарь и исходный список. Она создаёт копию вложенного списка, изменяет элемент уже в этой копии, затем создаёт новый словарь и кладёт туда обновлённый список.

Вариант через распаковку:

def update_list_item(data: dict, key: str, index: int, new_value) -> dict:
    new_list = [
        new_value if i == index else item
        for i, item in enumerate(data[key])
    ]

    return {
        **data,
        key: new_list
    }