Имена как привязки к объектам: почему '=' — это не memcpy
core предсказать, изменится ли объект через второе имя, ссылающееся на тот же список/словарь, без запуска кода
В C переменная — это именованная область памяти фиксированного размера и типа, выделенная компилятором на стеке или явно в куче. int x = 5; означает: зарезервировать несколько байт, положить туда битовое представление числа 5, и всё последующее x = ... — это memcpy-подобная запись новых байт в ту же самую ячейку. int y = x; выделяет новую ячейку и копирует в неё содержимое x: после этого x и y — два независимых хранилища. Присваивание в C — операция копирования значения в фиксированное место.
В Python имя не является ячейкой памяти. Имя — это привязка (запись в пространстве имён, по сути ключ в словаре), которая ссылается на объект, живущий в куче интерпретатора. x = 5 не резервирует «ячейку x» — оно создаёт (или находит) объект 5 и делает так, чтобы имя x указывало на него. Следующее x = "hello" не «перезаписывает x другим типом» — оно просто переставляет привязку имени на другой объект. Ни размер, ни тип имени никогда не были фиксированы, потому что у имени вообще нет ни размера, ни типа — тип есть у объекта.
Отсюда прямое следствие, которого нет в C: два разных имени могут ссылаться на один и тот же объект. Это называется aliasing. Ключевой вопрос, который нужно научиться задавать вместо привычного «скопировалось ли значение» — изменяемый (mutable) объект или неизменяемый (immutable)?
- Если объект изменяемый (
list,dict,set, большинство пользовательских классов) — мутация через любое из имён видна через все остальные имена, ссылающиеся на тот же объект. - Если объект неизменяемый (
int,float,str,tuple,frozenset) — у него нет операций, меняющих его «изнутри». Любая операция, похожая на изменение (b = b + 1), создаёт новый объект и перепривязывает имя к нему.
Функция id() возвращает идентификатор объекта (в CPython он связан с адресом объекта в памяти) и позволяет проверить, ссылаются ли два имени на один объект.
graph LR
subgraph "C: имя = ячейка"
X["x: int, адрес 0x100"] -->|memcpy| V1["байты: 5"]
end
subgraph "Python: имя = привязка"
A["a"] --> O["объект int(5)"]
B["b"] --> O
endРазобранный пример
a = [1, 2, 3]
b = a # b и a — два имени одного и того же объекта list
b.append(4)
print(a) # что здесь?
Рассуждение по-сишному подсказывает: «b = a — это копия структуры, значит a не тронут». Это неверно. [1, 2, 3] — объект в куче. a и b — два имени, привязанные к этому же объекту. b.append(4) мутирует объект in place, не создавая новый, поэтому изменение видно через оба имени: a тоже станет [1, 2, 3, 4]. Проверить это можно через id(a) == id(b) — идентификаторы совпадают.
Настоящая независимая копия требует явного действия:
c = a.copy() # новый объект-список с тем же содержимым
c.append(5)
print(a) # [1, 2, 3, 4] — оригинал не тронут
Сравните с неизменяемым случаем:
a = 1000
b = a
b = b + 1
print(a) # 1000 — не изменилось
Тут b + 1 создаёт новый объект int(1001) и перепривязывает b к нему. a продолжает ссылаться на старый объект 1000. Мутации не было — была перепривязка одного из двух имён.
Попробуй сейчас
Не запуская код, определите, что выведет каждый print:
d1 = {"count": 0}
d2 = d1
d2["count"] = 5
s1 = "hi"
s2 = s1
s2 = s2 + "!"
t1 = (1, [2, 3])
t2 = t1
t2[1].append(4)
print(d1["count"]) # ?
print(s1) # ?
print(t1) # ?
Ожидаемый результат: 5, "hi", (1, [2, 3, 4]). Первый и третий случай — мутация общего изменяемого объекта (dict; и вложенный list внутри неизменяемого tuple — сам tuple неизменяем, но хранит ссылку на изменяемый список, и мутируется именно список). Второй случай — str неизменяем, s2 + "!" создал новый объект, s1 не тронут.
Получилось, если…
Вы разобрались, если для любой пары «имя A, имя B, ссылающихся на один объект» можете сразу сказать: изменение через B видно через A только если объект изменяемый и операция мутирует его in place (.append, [key] =, .sort()), а не создаёт новый объект и перепривязывает B. И если понимаете, зачем нужен явный .copy().
Вывод
= в Python — это привязка имени к объекту, а не копирование байт. Aliasing (несколько имён → один объект) — нормальное состояние программы, а не побочный эффект. «Увидят» ли другие имена изменение, решают два вопроса: mutable или immutable объект, и мутирует ли операция объект in place или создаёт новый.
AI-generated · source-grounded review
🛡 Fact-checked: 5 risky claims verified · 4 removed · confidence: high · figures: 2
A second model re-checked this lesson's claims and corroborated 5 of 7 , and could not confirm 2 either way. This is incomplete corroboration, not a disagreement.
On your own course these buttons answer instantly, quizzes track what you've mastered, and lessons adapt to your gaps. Write my course