> Что означает изменяемость и неизменяемость объектов в Python (Python)

Уровень: senior · Роль: backend · Язык: Python · Категория: Технические вопросы

Компании: MirrorApp, АО НПФ Будущее, Сбер, Исходный Код, Sunlight, mozen

Стек: Python

> Пример ответа

Короткий ответ

Изменяемость (mutable) означает, что объект можно модифицировать после создания, сохраняя его идентичность (id). Неизменяемость (immutable) - объект нельзя изменить, любая операция создаёт новый объект. В Python неизменяемы: int, float, str, tuple, frozenset, bytes. Изменяемы: list, dict, set, bytearray, а также пользовательские классы по умолчанию. Это фундаментальное свойство, влияющее на хешируемость, передачу в функции, безопасность данных и производительность.

Подробное объяснение

Неизменяемость в Python - это не просто "нельзя поменять", а гарантия того, что объект никогда не изменит своё состояние. При попытке модификации интерпретатор создаёт новый объект. Например, s = s + "a" для строки создаёт новую строку, а l.append(1) для списка мутирует существующий.

Ключевые следствия:

  1. Хешируемость: неизменяемые объекты могут быть ключами dict и элементами set, так как их хеш стабилен. Изменяемые - нет, иначе нарушится инвариант хеш-таблиц.

  2. Передача в функции: неизменяемые передаются "по значению" (фактически по ссылке, но модификация невозможна), изменяемые - "по ссылке" с возможностью мутации.

  3. Копирование: для неизменяемых copy.copy и copy.deepcopy часто возвращают тот же объект (оптимизация), для изменяемых - реальную копию.

  4. Производительность: неизменяемые объекты безопасны для конкурентного доступа, но операции с ними (особенно конкатенация строк в цикле) могут быть дорогими из-за постоянного создания новых объектов.

  5. Идентичность: is для неизменяемых может давать true для разных переменных (интернирование малых int и строк), для изменяемых - всегда false для разных объектов.

Важно: кортеж, содержащий список, формально неизменяем, но его содержимое можно изменить через ссылку на список. Неизменяемость - свойство самого объекта, а не его содержимого.

На практике

В реальном коде это проявляется в:

  • Дизайне API: возвращайте неизменяемые структуры (tuple вместо list) для защиты данных от случайной модификации.
  • Ключах dict: используйте только неизменяемые типы, иначе получите TypeError: unhashable type.
  • Аргументах по умолчанию: никогда не используйте изменяемые объекты как default values - они разделяются между вызовами.
  • Функциональном стиле: предпочитайте возврат новых объектов вместо мутации входных, это упрощает тестирование и отладку.
  • Сравнении: == для неизменяемых сравнивает значения, для изменяемых - тоже значения, но is для изменяемых почти всегда false.

Для пользовательских классов можно реализовать неизменяемость через __slots__, переопределение __setattr__ или использование dataclass(frozen=True).

Пример кода

PYTHON
# Неизменяемые
s = "hello"
# s[0] = "H" # TypeError: 'str' object does not support item assignment
s2 = s.upper() # создаётся новый объект
t = (1, 2, [3])
# t[0] = 10 # TypeError
t[2].append(4) # работает! кортеж неизменяем, но список внутри изменяем
# Изменяемые
lst = [1, 2]
lst.append(3) # мутация, id(lst) не меняется
print(id(lst)) # тот же id
# Хешируемость
d = {1: "a", (1, 2): "b"} # ok
# d = {[1, 2]: "c"} # TypeError: unhashable type: 'list'
# Аргумент по умолчанию - классическая ошибка
def add_item(item, container=[]):
container.append(item)
return container
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] - баг!
# Правильно:
def add_item(item, container=None):
if container is None:
container = []
container.append(item)
return container
# frozen dataclass
from dataclasses import dataclass
@dataclass(frozen=True)
class Point:
x: int
y: int
p = Point(1, 2)
# p.x = 3 # FrozenInstanceError

Как отвечать на собеседовании

Начните с чёткого определения, приведите примеры типов. Затем перейдите к практическим следствиям: хешируемость, передача в функции, безопасность. Обязательно упомяните нюанс с кортежем, содержащим изменяемый объект - это показывает глубину понимания. Если спросят про производительность, объясните trade-off: неизменяемость даёт безопасность, но может быть дороже при частых модификациях. Хорошо добавить пример с dataclass(frozen=True) как современный способ создания неизменяемых объектов. Для senior-позиции стоит упомянуть, как неизменяемость связана с конкурентным программированием и функциональным стилем.

Что проверяет интервьюер

Интервьюер оценивает:

  • Понимание фундаментального различия между изменяемыми и неизменяемыми типами, а не заучивание списка.
  • Умение объяснить, почему неизменяемые объекты можно использовать как ключи dict, а изменяемые нет.
  • Знание подводных камней: изменяемые аргументы по умолчанию, кортеж со списком.
  • Понимание, что неизменяемость - это свойство объекта, а не переменной.
  • Способность применить знание на практике: выбор типа данных под задачу, проектирование API.
  • Для senior - осознание связи с многопоточностью, кэшированием, функциональным программированием.

Типичные ошибки

  • Утверждение, что кортеж всегда полностью неизменяем - забывают про вложенные изменяемые объекты.
  • Использование изменяемых объектов как аргументов по умолчанию - классическая ошибка, которую часто приводят как пример.
  • Путаница между == и is при работе с неизменяемыми объектами (интернирование строк и малых int).
  • Мнение, что неизменяемость означает "нельзя изменить значение переменной" - на самом деле переменная может быть переназначена, неизменяем сам объект.
  • Игнорирование того, что пользовательские классы по умолчанию изменяемы, если явно не реализовать неизменяемость.
  • Непонимание, что frozenset неизменяем, но его элементы должны быть хешируемыми (и неизменяемыми).

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью