Как работает механизм присваивания и передачи объектов по ссылке в Python?

«Как работает механизм присваивания и передачи объектов по ссылке в Python?» — вопрос из категории Python, который задают на 26% собеседований Data Scientist / ML Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В Python переменные — это имена (names), которые ссылаются на объекты (objects) в памяти. Присваивание (=) связывает имя с объектом, а не копирует сам объект. Это называется передачей по ссылке на объект.

Ключевое правило: «Объекты не копируются при присваивании».

Пример 1: Изменяемые объекты (списки, словари, множества)

a = [1, 2, 3]  # Создается список, `a` ссылается на него
b = a          # `b` теперь ссылается на ТОТ ЖЕ объект в памяти

a.append(4)    # Изменяем объект через ссылку `a`
print(a)       # [1, 2, 3, 4]
print(b)       # [1, 2, 3, 4] - `b` «видит» изменения, так как ссылается на тот же объект
print(a is b)  # True - это один и тот же объект (проверка по id)

Пример 2: Неизменяемые объекты (числа, строки, кортежи)

x = 10  # `x` ссылается на объект `int` со значением 10
y = x   # `y` ссылается на тот же объект `10`
x = x + 5  # Операция создает НОВЫЙ объект `int(15)`. `x` теперь ссылается на него.
print(x)   # 15
print(y)   # 10 - `y` продолжает ссылаться на старый объект `10`

С неизменяемыми объектами безопасно, так как их нельзя изменить «на месте».

Создание независимых копий:

  • Поверхностное копирование (copy()): Создает новый контейнер, но вкладывает в него ссылки на те же вложенные объекты.
    import copy
    list1 = [1, [2, 3]]
    list2 = copy.copy(list1)  # Поверхностная копия
    list1[0] = 100  # Изменяется только list1
    list1[1].append(4)  # Изменяется вложенный список, что затронет list2!
    print(list1)  # [100, [2, 3, 4]]
    print(list2)  # [1, [2, 3, 4]]  # Вложенный список общий!
  • Глубокое копирование (deepcopy()): Рекурсивно создает новые копии всех объектов, включая вложенные.
    list3 = copy.deepcopy(list1)  # Глубокая копия
    list1[1].append(5)
    print(list1)  # [100, [2, 3, 4, 5]]
    print(list3)  # [100, [2, 3, 4]]  # Полностью независимая копия

Практический вывод: При передаче изменяемых объектов в функции или при присваивании помните, что вы работаете с исходным объектом. Если нужна независимая копия — используйте copy или deepcopy.