Геттеры и сеттеры в Python
Постигаем геттеры и сеттеры
Для начала разберемся, что это такое и когда они нам могут пригодиться. Простыми словами, геттер получает значение приватного атрибута, а сеттер его устанавливает, вот и все.
С их помощью можно поддержать инкапсуляцию (получать доступ к приватным атрибутам) или каким-либо дополнительным образом провалидировать или обработать значения.
Пара слов о property
Примеры сразу будем рассматривать с удобными аннотациями @property
- свойствами. Это более "питонический" путь работы с поведением атрибутов, такими как получение, настройка и удаление.
Свойства используются так же, как и обычные атрибуты, но предоставляют возможность добавлять поведение при доступе к ним.
Как связаны свойства с геттерами и сеттерами? Когда мы получаем доступ к свойству, то автоматически вызывается связанный с ним getter-метод. Когда мы пытаемся изменить свойство, соответственно будет вызван setter-метод.
Пример использования
Создадим класс Product
, чтобы мы могли провернуть с ним некоторые процедуры:
- создать конкретный продукт, задав ему имя и базовую стоимость (без НДС)
- получить стоимость продукта с / без НДС
- назначить новую цену, введя сумму с НДС, выполнить расчет базовой стоимости и записать ее в приватный атрибут.
class Product: def __init__(self, name: str, base_price: float) -> None: self.name = name self.__base_price = base_price self.__vat_percent = 0.13 @property def price(self) -> float: return round(self.__base_price * (1 + self.__vat_percent), 2) @price.setter def price(self, value: float) -> None: # пересчитываем базовую цену, т.к. передаем в value цену с НДС self.__base_price = round(value / (1 + self.__vat_percent), 2) @property def base_price(self) -> float: return self.__base_price
Сразу определим, что НДС будет равен 13%.
Под капотом некоторые обработки для свойств:
- Округление и рассчет цены с НДС при получении цены через свойство
price
- Рассчет базовой стоимости по полученной стоимости с НДС, округление и установка значения в приватный атрибут
__base_price
Класс описали, посмотрим теперь на пример использования:
coffee = Product(name="Флэт уайт", base_price=300) # Создаем наш кофе с ценой до НДС = 300 р. print(coffee.name) # Флэт уайт | Здесь просто название print(coffee.price) # 339.0 | Цена рассчитана с НДС, это произошло автоматически coffee.price = 350 # Устанавливаем новую цену для кофе, сумма сразу с НДС, чтобы произошла корректная обработка и вычисление базовой стоимости print(coffee.price) # >>> 349.99 | Получаем цену с НДС. Не равно 350 из-за округлений при записи print(coffee.base_price) # >>> 309.73 | Базовая цена без НДС
Пример получился лаконичным, так как мы производим все вычисления, просто обращаясь к свойствам, не вызываем явные методы для этих операций. При этом, напрямую не обращаемся к приватным атрибутам, не нарушая принцип инкапсуляции.
Маленький бонус: deleter
Хоть в нашем примере это не имеет особого смысла, но для рассмотрения еще 1 свойства можно попробовать обновить пример так, чтобы использовать deleter. Это свойство уместно, когда нужно удалить атрибут или сбросить его в изначальное состояние, или обработать удаление атрибута каким-то определенным образом.
Добавим в наш пример с Product следующее свойство:
@price.deleter def price(self): self.__base_price = 0
Тогда если мы удалим атрибут, значение __base_price
просто сбросится в ноль. Проверим:
del coffee.price print(coffee.price) # 0.0
Выводы
✅ помогают скрыть реализацию и сохранить инкапсуляцию
✅ позволяют валидировать или изменять данные при доступе к ним
✅ обеспечивают простой и читаемый интерфейс (без явных методов)
Лучше использовать методы вместо свойств, если: