В КУРСЕ?

Разбираемся в теме

Список скопирован в Python: почему исходные данные всё равно меняются?

Ошибка с изменяющимся списком часто выглядит загадочно: программист работает с новой переменной, а старое значение тоже становится другим. Причина обычно связана с тем, какой объект доступен через каждое имя. Погружение в Python полезно начать с этой модели, потому что она объясняет поведение списков, словарей и аргументов функций. Рассмотрим небольшой пример с вложенными числовыми списками, который можно разобрать на бумаге.

Присваивание связывает имя с объектом

Если переменная original обозначает список, операция alias = original не создаёт независимого списка. Оба имени указывают на один объект. Добавление элемента через alias будет видно и через original. При этом последующее присваивание имени alias другого списка меняет именно связь этого имени: original продолжит обозначать прежний объект. Различие между изменением объекта и переназначением имени помогает объяснять такие ситуации без предположения, что Python самопроизвольно синхронизирует две копии.

Поверхностная копия отделяет внешний контейнер

Пусть original содержит два вложенных списка: [2, 4] и [6]. После shallow = original.copy() появляется новый внешний список, но его элементы продолжают ссылаться на прежние вложенные списки. Поэтому shallow[0].append(8) изменяет первый внутренний список, видимый из обеих структур. Зато shallow.append([10]) добавляет новый элемент только во внешний список shallow. В исходном внешнем списке элементов по-прежнему два. Важно проверять, на каком уровне выполняется изменение, а не просто искать слово copy в программе.

Глубокое копирование имеет определённую цель

Для такого набора вложенных списков copy.deepcopy(original) создаёт независимую вложенную структуру. Если выполнить его до описанных изменений, глубокая копия сохранит [2, 4] и [6]. Однако глубокое копирование не следует добавлять везде автоматически. Иногда общие объекты нужны по смыслу, а иногда достаточно создать новый внешний контейнер. Хороший вопрос звучит конкретно: какие изменения должны быть независимыми? Ответ определяет способ организации данных и помогает избежать лишнего копирования ради привычки.

Проверьте изменяемые значения по умолчанию

Похожая неожиданность возникает, когда пустой список указан прямо как значение параметра при определении функции. Такое значение создаётся при определении, поэтому последующие вызовы без этого аргумента могут использовать один список. Если функция добавляет запись, новая операция увидит прошлые записи. Для независимого списка в каждом вызове часто используют значение None, проверяют его внутри функции и там создают новый список. Но при явно переданном списке поведение нужно продумать отдельно: требуется изменить его или вернуть самостоятельный результат.

Попробуйте на практике

Проследите изменения четырёх переменных на бумаге или в собственном интерпретаторе Python. Все данные учебные и не связаны с файлами.

  1. Создайте original со значением [[2, 4], [6]]. Свяжите alias с original. Нарисуйте два имени и один общий внешний контейнер.
  2. Получите shallow методом copy, а deep через copy.deepcopy после импорта модуля copy. Сделайте это до любых дальнейших изменений.
  3. Выполните shallow[0].append(8). Запишите содержимое всех четырёх переменных и отметьте общий внутренний список.
  4. Выполните shallow.append([10]). Снова запишите результаты. Сравните число элементов внешнего списка original и shallow.
  5. Объясните каждый результат через ссылки и уровни вложенности. Затем предложите, где независимая копия нужна по задаче, а где общие данные допустимы.

Как проверить результат. После двух изменений original и alias равны [[2, 4, 8], [6]], shallow равен [[2, 4, 8], [6], [10]], а deep сохраняет [[2, 4], [6]]. Объяснение различает внешний контейнер и его вложенные элементы.

Частые вопросы

Доказывает ли равенство списков, что это один объект?

Нет. Равенство содержимого проверяют отдельно от идентичности. Два разных списка могут содержать одинаковые элементы; оператор is отвечает на вопрос об одном и том же объекте.

Поможет ли переименование переменной?

Название может сделать намерение понятнее, но не меняет отношения объектов. Нужно изменить способ создания структуры или явно определить, какие данные разрешено изменять совместно.

Самостоятельный разбор темы. Содержание конкретной обучающей программы здесь не представлено.

Зарегистрируйтесь, чтобы уточнить возможность доступа к этому материалу

Зарегистрироваться
← К списку материалов