Почему при очистке списка в Python удаляются его копии в других переменных
Очистка списка в Python может быть выполнена разными способами, некоторые из которых могут привести к неожиданным результатам, если вы не знаете, чем они различаются и что происходит под капотом.
Рассмотрим очень простой скрипт, который хотя и не делает ничего осмысленного, но продемонстрирует что имеется ввиду:
temp_list = list() dict_config = dict() for l in ['a', 'b', 'c']: for n in range(1,4): temp_list.append(l + str(n)) dict_config[l] = temp_list temp_list.clear() print(dict_config)
В этом скрипте создаётся список и словарь. Затем запускаются два цикла, один из которых вложенный. Вложенный цикл заполняет список, затем добавляет этот список в словарь (в качества ключа используется одно из итерируемых значений). Временный список очищается, чтобы использовать его снова и снова в этом же цикле, но уже с другими данными.
Какой результат вы ожидаете получить?
По задумке автора скрипта результатом должен стать словарь со следующим содержимым:
{'a': ['a1', 'a2', 'a3'], 'b': ['b1', 'b2', 'b3'], 'c': ['c1', 'c2', 'c3']}
А вот что действительно получено при выполнении скрипта?
Полученный результат:
{'a': [], 'b': [], 'c': []}
То есть словарь действительно создан, ключи в нём имеются, но значения (которыми должны были стать временные списки), пусты.
Этот пример является адаптацией фрагмента скрипта с более осмысленной функцией. Но, в любом случае, списки могут использоваться в качестве временных хранилищ динамических данных, поэтому нужно разобраться, в чём именно проблема.
В скрипте нет логической ошибки — проблема в способе очистки списка.
Как очистить список в Python
Списки в Python могут быть очищены несколькими способами, которые вызывают 2 возможных варианта:
1) Один из способов действует «на месте» (in-place), то есть очищает те ячейки памяти, в которых хранится содержимое списка). Даже после того, когда список сохранён в словарь в качестве значения, в словаре хранится не дубликат информации из списка, а ссылка на область памяти, где размещён список. Это приводит к тому, что если список очищен, то он также очищается и в словаре, поскольку это не дублированные данные, а всего лишь ссылка на область памяти, в которой теперь ничего нет.
2) Второй способ очистки просто создаёт новый объект списка и назначает его переменной с тем же именем (той же самой переменной). При таком способе очистки, ранее сохранённый в словарь список оказывается незатронутым.
Рассмотрим этот же самый скрипт, в котором строка
temp_list.clear()
Заменена на строку:
temp_list = []
То есть в качестве метода очистки выбрано присвоение нового пустого значения.
temp_list = list() dict_config = dict() for l in ['a', 'b', 'c']: for n in range(1,4): temp_list.append(l + str(n)) dict_config[l] = temp_list temp_list = [] print(dict_config)
В этом случае скрипт сработает как ожидается.
Очистка списка in-place и создание нового списка (реинициализация)
Итак, имеются следующие способы очистить список«на месте» (in-place):
temp_list.clear() del temp_list[:] temp_list*=0
При использовании этих способов очистки списка, очищаются области памяти, в которых хранится список, а также очищаются копии списка, которые являются ссылками на эту область память.
И имеется один способ удаления списка с помощью реинициализации:
temp_list = []
Повторное создание списка с этим же именем не приводит к потере копий списка, сохранённых в другие переменные.
Связанные статьи:
- Как запустить небольшой код Python в Bash (50%)
- Как запустить программу из Python: как выполнить системную команду или другой скрипт Python (полное руководство) (50%)
- Как добавить префикс «b» к имени переменной в Python? (50%)
- Решение проблемы со сломавшимся после обновления пакетов Pip (32.2%)
- Решение проблемы с ошибкой fatal error: libxml/xmlversion.h: Нет такого файла или каталога (32.2%)
- Как преобразовать строку в верхний регистр в Bash (RANDOM - 32.2%)