← Назад к вопросам
Как в 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 для сигнализации