Помогут ли copy или deepcopy при передаче списка словарей в Python
Что проверяют
Понимаете ли вы, что в функцию уходит ссылка на объект, а не его копия, и что уровней вложенности у копии тоже несколько. Ответ «да, помогут» без уточнения, какая именно копия, сразу показывает, что человек не разделяет поверхностное и глубокое копирование.
Как отвечать
Сначала назовите причину проблемы. Аргумент не копируется: функция получает ссылку на тот же список, и если внутри делается lst.append(...) или правится словарь, вызывающий код увидит изменение.
Дальше разведите варианты:
lst[:],list(lst),lst.copy(),copy.copy(lst)дают новый список, но словари внутри остаются теми же самыми. Добавление элемента в копию оригинал не тронет, а правка словаря тронет;copy.deepcopy(lst)рекурсивно копирует и список, и словари, и всё, что лежит глубже, включая вложенные списки. Оригинал становится полностью независимым;- лучший вариант, когда он возможен: не мутировать вход вовсе. Функция принимает данные и возвращает новый результат, тогда копия не нужна никому.
import copy
src = [{"tags": ["a"]}]
shallow = src[:]
shallow[0]["tags"].append("b") # src тоже изменился
deep = copy.deepcopy(src)
deep[0]["tags"].append("c") # src не изменилсяДобавьте, что глубокая копия не бесплатна: она обходит весь граф объектов, помнит уже скопированное и на больших структурах заметна и по времени, и по памяти.
Где спотыкаются
Считают, что поверхностная копия защищает. Она защищает только верхний уровень, и классическая ловушка выглядит так: шаблон словаря копируется в цикле, у каждой копии правится вложенный список, а меняется один общий объект у всех.
Второе: применяют глубокую копию ко всему подряд. В структуре часто лежит не только текст и числа: соединение с базой, открытый файл, блокировка. Такое копирование либо падает, либо создаёт бессмысленный дубль, и тогда поведение задаётся методом __deepcopy__ или явным исключением поля.
Третье: наступают на изменяемое значение по умолчанию. Запись вида def add(item, target=[]) вычисляется один раз при определении функции, и один и тот же список копится между вызовами. Пишут target=None и создают новый список внутри.