🔖 Справочник: инкапсуляция

⚡ Кратко

Используйте эту страницу как шпаргалку: найдите нужную конструкцию, сравните синтаксис с примером и затем примените её в своём коде.

Соглашения об именовании

Имя Уровень доступа Назначение
name Публичный Открытый интерфейс, доступен отовсюду.
_name Защищённый Внутреннее использование, доступен в наследниках.
__name Приватный Строго внутреннее, манглируется в _ClassName__name.

Геттер и сеттер вручную

class Temperature:
    def __init__(self) -> None:
        self.__celsius = 0

    def get_celsius(self) -> float:
        return self.__celsius

    def set_celsius(self, value: float) -> None:
        if value < -273.15:
            raise ValueError("Слишком низкая температура")
        self.__celsius = value

Свойство через @property

class Temperature:
    def __init__(self, value: float) -> None:
        self.__celsius = 0
        self.celsius = value

    @property
    def celsius(self) -> float:
        return self.__celsius

    @celsius.setter
    def celsius(self, value: float) -> None:
        if value < -273.15:
            raise ValueError("Слишком низкая температура")
        self.__celsius = value

Только для чтения

class Circle:
    def __init__(self, radius: float) -> None:
        self.__radius = radius

    @property
    def radius(self) -> float:
        return self.__radius

    @property
    def area(self) -> float:
        return 3.14159 * self.__radius ** 2

Свойство без сеттера нельзя изменить извне.

Дескриптор: методы протокола

Метод Когда вызывается Аргументы
__set_name__(self, owner, name) При создании класса, где объявлен дескриптор owner — класс, name — имя атрибута
__get__(self, instance, owner) Чтение атрибута: obj.attr instance — объект (None при доступе через класс)
__set__(self, instance, value) Запись: obj.attr = value value — присваиваемое значение
__delete__(self, instance) Удаление: del obj.attr

Универсальный дескриптор-валидатор

class PositiveNumber:
    def __set_name__(self, owner, name: str) -> None:
        self.private_name = "_" + name

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return getattr(instance, self.private_name)

    def __set__(self, instance, value: float) -> None:
        if value < 0:
            raise ValueError("Значение не может быть отрицательным")
        setattr(instance, self.private_name, value)

    def __delete__(self, instance) -> None:
        delattr(instance, self.private_name)


class Person:
    age = PositiveNumber()
    balance = PositiveNumber()

    def __init__(self, age: int, balance: float = 0) -> None:
        self.age = age
        self.balance = balance

Значение хранится под именем _age / _balance; обращение идёт по именам age и balance.

Приватный метод

class User:
    def __init__(self, name: str) -> None:
        self.name = name
        self.__id = self.__generate_id()

    def __generate_id(self) -> str:
        return f"user-{id(self)}"

Проверка принадлежности к классу

Функция/атрибут Назначение
obj.__dict__ Словарь атрибутов объекта, включая манглированные имена.
Class.__mro__ Порядок разрешения методов (менее связан, но полезен в сочетании).