Что произойдет при изменении второго элемента списка b, если b = a
Что проверяют
Разделяете ли вы имя и объект. Это фундамент модели памяти Python, и от него зависит, будете ли вы ловить неожиданные изменения данных в чужом коде.
Как отвечать
b = a не копирует список, а привязывает второе имя к тому же самому объекту. Проверить легко: a is b истинно, идентификаторы совпадают. Значит b[1] = 42 меняет тот единственный список, и обращение через a покажет изменение.
a = [1, 2, 3]
b = a
b[1] = 42
a # [1, 42, 3]
a is b # True
b = b + [4] # новый объект, связь разорвана
a # [1, 42, 3]
b # [1, 42, 3, 4]Дальше стоит разделить два действия. Мутация объекта на месте, то есть присваивание по индексу, append, sort, extend, видна через оба имени. Переприсваивание имени просто перенаправляет это имя на другой объект и оригинал не трогает. Отсюда и разное поведение двух похожих строк: b += [4] мутирует общий список, а b = b + [4] создаёт новый.
Копия делается явно: a.copy(), a[:], list(a), а при вложенных изменяемых элементах copy.deepcopy(a).
Где спотыкаются
Переносят опыт с числами и строками. Там связывание работает точно так же, но эффекта не видно, потому что эти объекты неизменяемы: b += 1 создаёт новый объект и перепривязывает имя.
Второе: считают срез a[:] универсальным решением. Он создаёт новый список верхнего уровня, а вложенные списки и словари в нём остаются общими, и проблема возвращается уровнем глубже.
Третье: не переносят правило на аргументы функций. Передача списка в функцию это то же связывание имени, поэтому lst.append(1) внутри меняет список у вызывающего, а lst = lst + [1] в том же месте не меняет ничего снаружи. Разница неочевидна ровно до первого бага.