Почему число нельзя изменить так же, как список?

В предыдущей статье мы увидели довольно странное на первый взгляд поведение:

a = [1, 2]
b = a

a.append(3)

print(b)  # [1, 2, 3]

Создаётся ощущение, будто Python “связывает” переменные между собой. На самом деле никакой связи между переменными нет - есть только один объект, к которому привязано несколько имён.

Но теперь возникает очень естественный вопрос.

Если список можно изменить “на месте”, почему тогда с числами всё ведёт себя иначе?

Рассмотрим похожий пример:

x = 10
y = x

x = 20

print(y)

И здесь результат уже совершенно другой:

10

Переменная y осталась равной 10, хотя мы явно изменили x.

Почему так происходит?

Чтобы ответить на этот вопрос, важно заметить одну тонкую, но ключевую деталь: в Python есть разные типы объектов, и они по-разному ведут себя при “изменении”.

Начнём с чисел.

Когда мы пишем:

x = 10

создаётся объект числа 10, и имя x начинает на него ссылаться:

x ───► 10

Теперь выполняется строка:

x = 20

Здесь важно не путать происходящее с изменением объекта.

Число 10 не превращается в 20. Объект 10 вообще не меняется - он остаётся тем же самым объектом.

Вместо этого имя x просто перестаёт ссылаться на старый объект и начинает ссылаться на новый:

x ───► 20

y ───► 10

Объект 10 при этом продолжает существовать, потому что на него всё ещё ссылается переменная y.

Теперь посмотрим на список.

a = [1, 2]

Создаётся один объект списка:

a ───► [1, 2]

А теперь ключевой момент:

a.append(3)

Эта операция не создаёт новый список.

Она изменяет уже существующий объект “на месте”.

После этого:

a ───► [1, 2, 3]

Если раньше у нас было два имени:

a = [1, 2]
b = a

то схема выглядела так:

a ─┐
   ├──► [1, 2]
b ─┘

И когда мы вызываем:

a.append(3)

мы изменяем сам объект, а не имя. Поэтому оба имени продолжают указывать на один и тот же, уже изменённый список:

a ─┐
   ├──► [1, 2, 3]
b ─┘

Теперь мы подходим к главному различию.

Число 10 нельзя изменить.

Список можно.

Это не случайность и не особенность синтаксиса - это фундаментальное свойство объектов в Python.

Одни объекты после создания не могут измениться вообще. Любая “попытка изменения” приводит к созданию нового объекта и переназначению имени.

Другие объекты позволяют менять своё внутреннее состояние без создания нового объекта.

Именно поэтому:

  • число ведёт себя как “неизменяемый объект”
  • список ведёт себя как “изменяемый объект”

Попробуем собрать это в одну интуитивную картину.

Если объект неизменяемый, то он как будто “застывает” в своём состоянии. Если нужно новое значение - появляется новый объект.

Если объект изменяемый, то он остаётся тем же самым объектом, но его содержимое можно менять внутри.

И теперь становится понятно, почему поведение с числами и списками различается:

  • в случае чисел мы всегда работаем с новым объектом при изменении значения
  • в случае списков мы меняем уже существующий объект

И тут возникает важное следствие, которое мы будем постоянно видеть дальше:

Поведение программы в Python определяется не тем, что “лежит в переменной”, а тем, изменяем ли мы объект или просто переназначаем имя.

И теперь у нас наконец появляется возможность назвать вещи своими именами.

То, что мы наблюдали в предыдущих статьях, называется:

  • изменяемые объекты (mutable)
  • неизменяемые объекты (immutable)

Но теперь это уже не абстрактные термины.

Это просто названия того поведения, которое мы уже увидели на практике.

В следующей статье мы как раз аккуратно введём эти понятия и соберём всю картину целиком: какие объекты бывают в Python и почему это деление настолько важно для всего языка.