Как создать неизменяемый (immutable) класс в Python

«Как создать неизменяемый (immutable) класс в Python» — вопрос из категории ООП, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Неизменяемый (immutable) объект — это объект, состояние которого нельзя изменить после его создания. Это повышает предсказуемость и безопасность кода, особенно в многопоточных приложениях, а также позволяет использовать объекты в качестве ключей словаря.

В Python есть несколько способов создать неизменяемый класс:

1. @dataclass(frozen=True) (Python 3.7+)

Это самый современный и предпочтительный способ. Декоратор dataclass с параметром frozen=True автоматически делает класс неизменяемым.

from dataclasses import dataclass, FrozenInstanceError

@dataclass(frozen=True)
class Point:
    x: int
    y: int

p = Point(10, 20)
print(p.x)  # 10

try:
    p.x = 30  # Попытка изменить атрибут
except FrozenInstanceError as e:
    print(e)  # Вывод: cannot assign to field 'x'

2. collections.namedtuple

Это фабричная функция для создания простых классов-кортежей. Объекты namedtuple легковесны и неизменяемы по своей природе.

from collections import namedtuple

Point = namedtuple('Point', ['x', 'y'])

p = Point(10, 20)
print(p.x) # 10

# Попытка изменения вызовет AttributeError
# p.x = 30 -> AttributeError: can't set attribute

3. Ручная реализация через __slots__ и __setattr__

Этот способ дает полный контроль, но является наиболее многословным. Он полезен для понимания механики неизменяемости.

class ImmutablePoint:
    __slots__ = ('x', 'y') # Экономит память и предотвращает создание __dict__

    def __init__(self, x, y):
        # Используем object.__setattr__ для инициализации полей
        super().__setattr__('x', x)
        super().__setattr__('y', y)

    def __setattr__(self, name, value):
        # Переопределяем метод, чтобы запретить изменения
        raise AttributeError(f"Can't modify attribute {name}")