Изменяемые и неизменяемые типы: list против tuple

Разбираем вопрос, с которого часто начинают Python-секцию интервью: какие типы можно менять «на месте», а какие нет.

Изменяемый (mutable) объект можно менять, не создавая новый: его содержимое правится по тому же адресу в памяти. Неизменяемый (immutable) объект после создания изменить нельзя — любая «правка» создаёт новый объект.

Вопрос: какие типы изменяемые, а какие нет?

Чёткий ответ. Изменяемые: list, dict, set, bytearray. Неизменяемые: int, float, bool, str, tuple, frozenset, bytes, None. Проверить легко: попробуйте изменить элемент.

lst = [1, 2, 3]
lst[0] = 99          # список меняется на месте
print(lst)

t = (1, 2, 3)
try:
    t[0] = 99        # кортеж изменить нельзя
except TypeError as e:
    print("Ошибка:", e)

Вывод:

[99, 2, 3]
Ошибка: 'tuple' object does not support item assignment

Почему это важно: один объект — несколько имён

Имя в Python — это ссылка на объект, а не «коробка со значением». Когда вы пишете b = a, обе переменные указывают на один и тот же объект. Для неизменяемых типов это безопасно, а для изменяемых — источник классических багов: правка через одно имя видна через другое.

a = [1, 2, 3]
b = a                # b и a — одно и то же
b.append(4)
print("a:", a)       # изменили b — но a тоже изменился
print("a is b:", a is b)

Вывод:

a: [1, 2, 3, 4]
a is b: True

С неизменяемыми типами такой ловушки нет: n = 5; m = n; m += 1 не трогает n, потому что m += 1 создаёт новый объект и переназначает имя.

Зачем вообще нужны неизменяемые типы

  • Ключи словаря и элементы множества. Они должны быть хешируемыми, а хешируемость требует неизменяемости. Поэтому ключом может быть tuple, но не list.
  • Безопасность. Неизменяемый объект нельзя случайно испортить из чужого кода — его можно свободно передавать и кешировать.
  • Скорость и память. Интерпретатор может переиспользовать неизменяемые объекты (например, кешировать малые числа).
d = {(1, 2): "точка"}     # tuple как ключ — ок
print(d[(1, 2)])
try:
    bad = {[1, 2]: "x"}   # list как ключ — нельзя
except TypeError as e:
    print("Ошибка:", type(e).__name__)
    print("list — нехешируемый тип")

Вывод:

точка
Ошибка: TypeError
list — нехешируемый тип

Итог

  • Изменяемые: list, dict, set. Неизменяемые: числа, str, tuple, frozenset.
  • Присваивание копирует ссылку, а не объект — для mutable это создаёт общие данные между именами.
  • Ключом словаря и элементом множества может быть только неизменяемый (хешируемый) объект.
Проверьте себя
1. Какой из типов изменяемый (mutable)?
Atuple
Bstr
Clist
Dfrozenset
2. Почему list нельзя использовать как ключ словаря?
AСписки слишком большие
BЭто запрещено синтаксисом
CСписки медленнее кортежей
DСписок изменяем и потому нехешируем
3. Что выведет b после a = [1,2]; b = a; a.append(3)?
A[1, 2, 3]
B[1, 2]
CОшибку
D[3]