Разберите данный фрагмент Python: "a = [1,2,3]; b = a; b.append(4); print(a)" — почему выводится то, что выводится, и какие приёмы используют для избежания побочных эффектов при работе со списками?
Код: `a = [111, 222, 333]; b = a; b.append(444); print(a)` Почему выводится [1,2,3,4][1,2,3,4][1,2,3,4]: - В Python присваивание имен переменным копирует ссылку на объект, а не сам объект. После `b = a` оба имени указывают на один и тот же список. - `b.append(4)` изменяет список "на месте" (in-place). Так как `a` и `b` — ссылки на один объект, изменение видно через оба имени. Поэтому `print(a)` покажет [1,2,3,4][1,2,3,4][1,2,3,4]. Как избегать побочных эффектов при работе со списками: - Поверхностное копирование (копирует сам список, но не вложенные объекты): - срез: `b = a[:]` - конструктор: `b = list(a)` - метод: `b = a.copy()` - модуль copy: `import copy; b = copy.copy(a)` - Глубокое копирование (копирует также вложенные структуры) — когда список содержит списки или изменяемые объекты: - `import copy; b = copy.deepcopy(a)` - Создавать новый список через генератор/компрехеншн: `b = [x for x in a]` - Использовать неизменяемые структуры (tuple): `t = tuple(a)` — нельзя изменить элементы, только создать новый кортеж. - Функциональный подход: не изменять входной список внутри функции, а возвращать новый список. Замечание о shallow vs deep: при вложенных списках поверхностное копирование не защитит вложенные элементы. Пример: если `a = [[111]]; b = a.copy(); b[0].append(222)` — то `a` изменится тоже (станет `[[1,2]]`), поэтому в таких случаях нужен `copy.deepcopy`.
Почему выводится [1,2,3,4][1,2,3,4][1,2,3,4]:
- В Python присваивание имен переменным копирует ссылку на объект, а не сам объект. После `b = a` оба имени указывают на один и тот же список.
- `b.append(4)` изменяет список "на месте" (in-place). Так как `a` и `b` — ссылки на один объект, изменение видно через оба имени. Поэтому `print(a)` покажет [1,2,3,4][1,2,3,4][1,2,3,4].
Как избегать побочных эффектов при работе со списками:
- Поверхностное копирование (копирует сам список, но не вложенные объекты):
- срез: `b = a[:]`
- конструктор: `b = list(a)`
- метод: `b = a.copy()`
- модуль copy: `import copy; b = copy.copy(a)`
- Глубокое копирование (копирует также вложенные структуры) — когда список содержит списки или изменяемые объекты:
- `import copy; b = copy.deepcopy(a)`
- Создавать новый список через генератор/компрехеншн: `b = [x for x in a]`
- Использовать неизменяемые структуры (tuple): `t = tuple(a)` — нельзя изменить элементы, только создать новый кортеж.
- Функциональный подход: не изменять входной список внутри функции, а возвращать новый список.
Замечание о shallow vs deep: при вложенных списках поверхностное копирование не защитит вложенные элементы. Пример: если `a = [[111]]; b = a.copy(); b[0].append(222)` — то `a` изменится тоже (станет `[[1,2]]`), поэтому в таких случаях нужен `copy.deepcopy`.