← Назад к вопросам
Является ли slicing списка shallow или deep копией в Python?
2.0 Middle🔥 181 комментариев
#DevOps и инфраструктура#Django
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Slicing списка: shallow copy
Slicing (срезание) списка создаёт shallow copy (поверхностную копию). Это значит, что создаётся новый список с новыми ссылками на элементы, но сами элементы (если это объекты) не копируются. Это важное различие, которое часто путают разработчики.
Основное понимание
# Slicing создаёт новый список
original = [1, 2, 3, 4, 5]
sliced = original[1:4]
print(f"original: {original}")
print(f"sliced: {sliced}")
print(f"Это разные объекты: {original is sliced}") # True - разные объекты
# Но это shallow copy для объектов
nested = [[1, 2], [3, 4], [5, 6]]
sliced_nested = nested[0:2]
print(f"nested и sliced_nested - разные списки: {nested is sliced_nested}") # True
print(f"Но элементы - одни и те же: {nested[0] is sliced_nested[0]}") # True!
Shallow copy: проблемы при изменении
# Проблема 1: Изменение вложенного объекта
nested = [[1, 2], [3, 4], [5, 6]]
sliced = nested[0:2]
# Изменяем вложенный список через sliced
sliced[0][0] = 999
print(f"nested: {nested}") # [[999, 2], [3, 4], [5, 6]]
print(f"sliced: {sliced}") # [[999, 2], [3, 4]]
# Изменение повлияло на оригинальный список!
# Проблема 2: С простыми типами это не проблема
original = [1, 2, 3, 4, 5]
sliced = original[1:4]
sliced[0] = 999
print(f"original: {original}") # [1, 2, 3, 4, 5] - не изменился
print(f"sliced: {sliced}") # [999, 3, 4]
# Потому что целые числа immutable (неизменяемы)
Сравнение: слайсинг vs присваивание
# Слайсинг создаёт новый объект
original = [1, 2, 3]
sliced = original[:] # или original[0:len(original)]
print(f"original is sliced: {original is sliced}") # False - разные объекты
# Присваивание - это просто новая ссылка на тот же объект
reference = original
print(f"original is reference: {original is reference}") # True - один объект
# Изменение через reference влияет на original
reference[0] = 999
print(f"original: {original}") # [999, 2, 3]
print(f"reference: {reference}") # [999, 2, 3]
Shallow copy в деталях
# Shallow copy копирует только первый уровень
original = {
"name": "Alice",
"hobbies": ["reading", "coding"]
}
# Через слайсинг списка (для списков)
original_list = [["a", "b"], ["c", "d"]]
sliced_list = original_list[:] # Shallow copy
# Через copy() метод (для словарей)
import copy
shallow_dict = copy.copy(original)
print(f"original is shallow_dict: {original is shallow_dict}") # False
print(f"original[hobbies] is shallow_dict[hobbies]: {original[hobbies] is shallow_dict[hobbies]}") # True!
# Изменение nested структуры влияет на обе копии
shallow_dict["hobbies"].append("gaming")
print(f"original: {original}")
# {name: Alice, hobbies: [reading, coding, gaming]}
print(f"shallow_dict: {shallow_dict}")
# {name: Alice, hobbies: [reading, coding, gaming]}
Deep copy для полной независимости
import copy
# Deep copy копирует всё рекурсивно
original = {
"name": "Alice",
"hobbies": ["reading", "coding"],
"address": {"city": "NYC", "zip": "10001"}
}
deep = copy.deepcopy(original)
print(f"original is deep: {original is deep}") # False
print(f"original[hobbies] is deep[hobbies]: {original[hobbies] is deep[hobbies]}") # False!
print(f"original[address] is deep[address]: {original[address] is deep[address]}") # False!
# Изменения в deep не влияют на original
deep["hobbies"].append("gaming")
deep["address"]["city"] = "Boston"
print(f"original[hobbies]: {original[hobbies]}") # [reading, coding]
print(f"deep[hobbies]: {deep[hobbies]}") # [reading, coding, gaming]
print(f"original[address]: {original[address]}") # {city: NYC, zip: 10001}
print(f"deep[address]: {deep[address]}") # {city: Boston, zip: 10001}
Практические примеры slicing
# Пример 1: Различные способы создать shallow copy списка
original = [1, 2, 3, 4, 5]
# Способ 1: Слайсинг
copy1 = original[:] # Shallow copy
# Способ 2: Конструктор list()
copy2 = list(original) # Shallow copy
# Способ 3: copy модуль
import copy
copy3 = copy.copy(original) # Shallow copy
print(all(copy is not original and copy == original for copy in [copy1, copy2, copy3]))
# Пример 2: Слайсинг с шагом
original = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
sliced1 = original[::2] # [0, 2, 4, 6, 8]
sliced2 = original[1:8:2] # [1, 3, 5, 7]
sliced3 = original[::-1] # Реверс: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
print(f"[::2]: {sliced1}")
print(f"[1:8:2]: {sliced2}")
print(f"[::-1]: {sliced3}")
# Пример 3: Использование слайсинга для операций
data = [1, 2, 3, 4, 5]
# Первые N элементов
first_three = data[:3] # [1, 2, 3]
# Последние N элементов
last_two = data[-2:] # [4, 5]
# Всё кроме первого и последнего
middle = data[1:-1] # [2, 3, 4]
print(f"first_three: {first_three}")
print(f"last_two: {last_two}")
print(f"middle: {middle}")
Когда это важно
# Случай, когда shallow copy создаёт проблемы
class Student:
def __init__(self, name, grades):
self.name = name
self.grades = grades
students = [
Student("Alice", [80, 85, 90]),
Student("Bob", [75, 80, 85])
]
# Shallow copy списка студентов
students_copy = students[:] # или students.copy()
# Изменение оценок через копию влияет на оригинал!
students_copy[0].grades.append(95)
print(f"Оригинал Alice grades: {students[0].grades}") # [80, 85, 90, 95]
print(f"Copy Alice grades: {students_copy[0].grades}") # [80, 85, 90, 95]
# Решение: используйте deep copy
import copy
students_deep = copy.deepcopy(students)
students_deep[0].grades.append(100)
print(f"Оригинал Alice grades: {students[0].grades}") # [80, 85, 90, 95]
print(f"Deep copy Alice grades: {students_deep[0].grades}") # [80, 85, 90, 95, 100]
Выводы
- Слайсинг (
list[:]) создаёт shallow copy — новый контейнер, но ссылки на элементы остаются теми же - Shallow copy безопасна для неизменяемых типов (int, str, tuple)
- Shallow copy может привести к неожиданным изменениям, если элементы — это объекты или коллекции
- Deep copy (
copy.deepcopy()) создаёт полностью независимую копию со всеми вложенными объектами - Выбор между shallow и deep copy зависит от структуры данных и требований приложения