Копия списка: от поверхностной копии до глубокой

Раздел: Основы Python -> Операции со списками

Копирование списков в Python: обзор методов

Наиболее эффективный и читаемый способ создать копию списка - использовать встроенный метод copy(). Метод возвращает новый список, содержащий те же элементы, что и исходный. Это поверхностная копия (shallow copy).

original = [1, 2, 3]
copy_list = original.copy()
print(copy_list)  # [1, 2, 3]

Python list element (получение элемента списка в python)

[1, 2, 3]

Python add to list (добавление элемента в список)

Метод copy() работает за линейное время O(n) и явно указывает на намерение скопировать список. Рекомендуется для большинства случаев, когда нет вложенных изменяемых объектов.

Типичная ошибка: путать присваивание с копированием. Присваивание b = a не создает копию, а лишь связывает новое имя с тем же объектом. Изменения через b затронут a. Всегда используйте copy() или другие методы копирования, если требуется независимая копия.

Почему присваивание (b = a) не создает копию списка?

Присваивание не копирует данные, а создает еще одну ссылку на тот же список. Любые изменения (добавление, удаление, изменение элементов) через одну переменную будут видны через другую.

a = [1, 2, 3]
b = a
b.append(4)
print(a)  # [1, 2, 3, 4]

Python copy list (копирование списка в python)

[1, 2, 3, 4]

Python list extend (метод extend для списка)

Если нужно разделить изменения, необходимо создать копию.

Ошибка: новички часто полагают, что b = a создает копию. Это приводит к непреднамеренным изменениям исходного списка.

Как скопировать список с помощью среза [:]?

Срез без указания границ ([:]) возвращает новый список, содержащий все элементы исходного. Это также поверхностная копия.

original = [10, 20, 30]
copy_slice = original[:]
copy_slice[0] = 99
print(original)  # [10, 20, 30]
print(copy_slice) # [99, 20, 30]

Python list reverse (реверс списка в python)

[10, 20, 30]
[99, 20, 30]

Python empty list (создание пустого списка)

Срез удобен, когда нужно скопировать только часть списка (например, original[1:3]), но для полной копии тоже подходит. Читаемость ниже, чем у copy().

Проблема: при копировании вложенных списков срез создает поверхностную копию. Вложенные списки остаются общими объектами.

Можно ли использовать list() для копирования?

Конструктор list(), примененный к списку, создает новый список с теми же элементами. Это еще один способ поверхностного копирования.

original = [5, 6, 7]
copy_list = list(original)
copy_list.append(8)
print(original)  # [5, 6, 7]
print(copy_list) # [5, 6, 7, 8]

Python list slice (срезы списков в python)

[5, 6, 7]
[5, 6, 7, 8]

Python filter list (фильтрация списка с помощью filter)

Метод list() может принимать любой итерируемый объект, поэтому он универсален. Однако для копирования списка его использование избыточно и менее очевидно, чем copy().

Ошибка: использование list() не защищает от общих вложенных объектов.

Что такое поверхностная копия с помощью copy.copy()?

Функция copy.copy() из модуля copy создает поверхностную копию любого объекта, включая списки. Это альтернатива специфичным методам.

import copy
original = [1, [2, 3]]
shallow = copy.copy(original)
shallow[0] = 100
shallow[1].append(4)
print(original)  # [1, [2, 3, 4]]
print(shallow)   # [100, [2, 3, 4]]

Python list function (функции для работы со списками)

[1, [2, 3, 4]]
[100, [2, 3, 4]]

Python добавить элемент в массив (добавление элемента в конец списка (append) в python)

Изменение самого списка (замена элемента) не влияет на оригинал, но изменение вложенного объекта (добавление к внутреннему списку) затрагивает оба. copy.copy() используется, когда нужно единообразно копировать разные типы.

Проблема: поверхностная копия не изолирует вложенные изменяемые объекты. Для полной изоляции требуется глубокое копирование.

Как создать полностью независимую копию с вложенными списками?

Функция copy.deepcopy() рекурсивно копирует все объекты, создавая полностью независимую копию. Это необходимо, если список содержит другие списки, словари или другие изменяемые объекты.

import copy
original = [[1, 2], [3, 4]]
deep = copy.deepcopy(original)
deep[0].append(100)
print(original)  # [[1, 2], [3, 4]]
print(deep)      # [[1, 2, 100], [3, 4]]

элемент двумерного массива python (доступ к элементу двумерного массива (списка списков) в python)

[[1, 2], [3, 4]]
[[1, 2, 100], [3, 4]]

Python обратиться к элементу списка (обращение к элементу списка по индексу в python)

Глубокое копирование гарантирует, что изменения в копии не затронут оригинал, даже на уровне вложенных структур. Однако оно медленнее и может быть избыточным для списков с неизменяемыми элементами.

Ошибка: использование deepcopy() там, где достаточно поверхностной копии, снижает производительность. Также deepcopy может вызывать рекурсию при циклических ссылках, но модуль copy обрабатывает это.

Как скопировать список с помощью оператора распаковки (*)?

Оператор * в списковом литерале распаковывает элементы исходного списка в новый. Это также поверхностная копия.

original = [7, 8, 9]
unpacked = [*original]
unpacked[1] = 0
print(original)  # [7, 8, 9]
print(unpacked)  # [7, 0, 9]

как заменить элемент в списке python (замена элемента в списке по индексу (list[2] = new_value) в python)

[7, 8, 9]
[7, 0, 9]

Этот способ элегантен, но менее известен. Подходит для быстрого копирования, когда читаемость не критична.

Проблема: такая копия поверхностная. Для вложенных структур необходима deepcopy().

- Python максимальный элемент массива (поиск максимального элемента в списке с помощью max в python)
- Python одинаковые элементы списков (поиск одинаковых (повторяющихся) элементов в списках python)
- Find elements python (поиск элементов в списке по условию (list comprehension, filter) в python)

Расширенные примеры копирования списков

Здесь представлены менее распространенные, но полезные сценарии копирования списков в Python.

Пример 1: Копирование через генератор списка (list comprehension)

Пример
original = [1, 2, 3, 4]
copied = [x for x in original]
copied.append(5)
print(original)  # [1, 2, 3, 4]
print(copied)    # [1, 2, 3, 4, 5]
[1, 2, 3, 4]
[1, 2, 3, 4, 5]

Генератор списка создает поверхностную копию. Он полезен, если нужно одновременно применить преобразование (например, [x*2 for x in original]), но для простого копирования избыточен.

Пример 2: Копирование многомерного списка (матрицы) с помощью list(map(list, ...))

Пример
matrix = [[1, 2], [3, 4]]
shallow_of_rows = list(map(list, matrix))
shallow_of_rows[0][0] = 99
print(matrix)  # [[1, 2], [3, 4]] - не изменился? Проверим:
print(shallow_of_rows) # [[99, 2], [3, 4]]
# На самом деле matrix не изменился, потому что map(list, ...) создал новые списки для каждой строки.
# Но если внутри строк были изменяемые объекты, они остались бы общими.
[[1, 2], [3, 4]]
[[99, 2], [3, 4]]

Этот прием создает поверхностную копию каждого подсписка, то есть «глубину один» (один уровень). Для полной независимости требуется deepcopy.

Пример 3: Глубокое копирование с настраиваемым поведением через __copy__ и __deepcopy__

Пример
import copy
class MyObject:
    def __init__(self, value):
        self.value = value
    def __copy__(self):
        return MyObject(self.value)  # поверхностно (но здесь нет вложенных)
    def __deepcopy__(self, memo):
        return MyObject(copy.deepcopy(self.value, memo))
obj = MyObject([1,2,3])
original_list = [obj]
deep_list = copy.deepcopy(original_list)
deep_list[0].value.append(4)
print(original_list[0].value)  # [1,2,3] - не изменилось
print(deep_list[0].value)     # [1,2,3,4]
[1, 2, 3]
[1, 2, 3, 4]

Пользовательские классы могут переопределять __copy__ и __deepcopy__ для контроля процесса копирования.

Пример 4: Копирование списка с использованием copyreg для настройки глубокого копирования

Пример
import copy, copyreg
# Зарегистрируем функцию для копирования встроенного типа (например, list)
# Но обычно это не требуется. Покажем пример для кастомного типа.
class CustomList(list):
    def __copy__(self):
        return CustomList(self)
copyreg.pickle(CustomList, lambda x: (CustomList, (list(x),)))
original = CustomList([1,2,3])
copied = copy.copy(original)
print(type(copied))  # <class '__main__.CustomList'>
<class '__main__.CustomList'>

Этот механизм используется для поддержки копирования пользовательских коллекций.

Пример 5: Копирование с помощью itertools.tee (для итераторов, не списков)

Пример
import itertools
iterator = iter([1,2,3])
it1, it2 = itertools.tee(iterator, 2)
list1 = list(it1)
list2 = list(it2)
print(list1)  # [1,2,3]
print(list2)  # [1,2,3]
# Но это не копирование списка, а создание двух независимых итераторов из одного.
[1, 2, 3]
[1, 2, 3]

Метод tee полезен, когда нужно пройти по итератору несколько раз, но не является копированием списка.

Копирование списка в Python - comments

En
Python copy list (python)