← Назад к вопросам

Как в Python защитить объект от изменения?

2.0 Middle🔥 161 комментариев
#Python Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Способы защиты объекта от изменения в Python

Python предоставляет несколько механизмов для создания неизменяемых (immutable) объектов. Выбор зависит от уровня защиты и сложности.

1. Встроенные неизменяемые типы

Некоторые типы Python по природе неизменяемы:

# Кортежи (tuples) — неизменяемы
tuple_data = (1, 2, 3)
# tuple_data[0] = 10  # TypeError: tuple object does not support item assignment

# Строки
string = "hello"
# string[0] = "H"  # TypeError

# Frozenset
frozen = frozenset([1, 2, 3])
# frozen.add(4)  # AttributeError

Эти типы хешируемы и используются как ключи в словарях.

2. dataclasses с frozen=True

Самый элегантный способ для классов:

from dataclasses import dataclass

@dataclass(frozen=True)
class User:
    name: str
    email: str
    age: int

user = User("Alice", "alice@example.com", 30)
# user.name = "Bob"  # FrozenInstanceError

# Но можно использовать replace для создания нового объекта
from dataclasses import replace
new_user = replace(user, name="Bob")

3. NamedTuple

Легче, чем dataclass, но тоже неизменяемо:

from typing import NamedTuple

class Point(NamedTuple):
    x: int
    y: int

p = Point(10, 20)
# p.x = 15  # AttributeError

# Хеширования поддерживает
point_set = {p}  # Работает

4. Переопределение setattr и delattr

Для полного контроля над обычными классами:

class ImmutableUser:
    def __init__(self, name: str, email: str):
        object.__setattr__(self, "name", name)
        object.__setattr__(self, "email", email)
    
    def __setattr__(self, name, value):
        raise TypeError(f"Cannot modify {self.__class__.__name__}")
    
    def __delattr__(self, name):
        raise TypeError(f"Cannot delete attribute from {self.__class__.__name__}")

user = ImmutableUser("Alice", "alice@example.com")
# user.name = "Bob"  # TypeError: Cannot modify ImmutableUser

5. Property только для чтения

Защита атрибутов через свойства:

class Config:
    def __init__(self, api_key: str):
        self._api_key = api_key  # private атрибут
    
    @property
    def api_key(self) -> str:
        return self._api_key
    
    # Нет setter! Только getter

config = Config("secret123")
print(config.api_key)  # Работает
# config.api_key = "newkey"  # AttributeError: can't set attribute

6. Pydantic с frozen=True

Для валидации данных:

from pydantic import BaseModel, ConfigDict

class UserModel(BaseModel):
    model_config = ConfigDict(frozen=True)
    
    name: str
    email: str

user = UserModel(name="Alice", email="alice@example.com")
# user.name = "Bob"  # ValidationError

7. Snapshot через deepcopy

Создание снимка объекта:

import copy
from typing import Any

def make_immutable_snapshot(obj: Any):
    return copy.deepcopy(obj)

original_data = {"user": {"name": "Alice"}}
snap = make_immutable_snapshot(original_data)

original_data["user"]["name"] = "Bob"
print(snap["user"]["name"])  # Всё ещё Alice

8. Использование slots

Ограничиваем поля:

class User:
    __slots__ = ("name", "email")
    
    def __init__(self, name: str, email: str):
        self.name = name
        self.email = email

user = User("Alice", "alice@example.com")
# user.age = 30  # AttributeError

Сравнение подходов

  • tuple/frozenset — простые данные, хеширование
  • dataclass(frozen=True) — рекомендуется, элегантно
  • NamedTuple — для кортежей данных
  • setattr override — специальные кейсы
  • Property readonly — частичная защита
  • Pydantic frozen — с валидацией

Важно помнить

  • Неизменяемость объекта не гарантирует неизменяемость вложенных объектов
  • Python не дает настоящей защиты (можно обойти)
  • Главное — это соглашение и дизайн
  • Используй type hints для сигнализации