Ответ
Дескриптор в Python — это объект, который реализует один или несколько методов протокола дескрипторов: __get__
, __set__
или __delete__
. Он позволяет контролировать доступ к атрибутам другого класса (чтение, запись, удаление).
Почему используются дескрипторы? Они предоставляют мощный механизм для:
- Валидации данных: Проверка значений перед их присвоением атрибуту.
- Ленивой инициализации: Вычисление значения атрибута только при первом доступе.
- Управления доступом: Реализация логики при получении или установке атрибута.
- Повторного использования логики: Инкапсуляция логики доступа к атрибутам в отдельном, переиспользуемом объекте.
Методы протокола дескрипторов:
__get__(self, instance, owner)
: Вызывается при чтении атрибута.instance
: Экземпляр класса, к которому принадлежит атрибут (илиNone
, если доступ через класс).owner
: Класс, которому принадлежит атрибут.
__set__(self, instance, value)
: Вызывается при присвоении значения атрибуту.__delete__(self, instance)
: Вызывается при удалении атрибута.
Пример дескриптора (валидация числа):
class PositiveNumber:
def __set_name__(self, owner, name):
# Сохраняем имя атрибута, чтобы хранить значение в словаре экземпляра
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):
if not isinstance(value, (int, float)) or value <= 0:
raise ValueError("Значение должно быть положительным числом.")
setattr(instance, self.private_name, value)
class Product:
price = PositiveNumber() # Дескрипторный атрибут
def __init__(self, name, price):
self.name = name
self.price = price # Вызовет __set__ дескриптора
# Использование
p1 = Product("Laptop", 1200)
print(f"Product: {p1.name}, Price: {p1.price}") # Вызовет __get__
try:
p2 = Product("Monitor", -50) # Вызовет ValueError
except ValueError as e:
print(f"Ошибка: {e}")
p1.price = 1500.50 # Вызовет __set__
print(f"New price: {p1.price}")
# p1.price = "invalid" # Вызовет ValueError
Типы дескрипторов:
- Data descriptor (дескриптор данных): Реализует
__set__
или__delete__
(и обычно__get__
). Имеет приоритет над атрибутами в словаре экземпляра (instance.__dict__
). Пример:property
. - Non-data descriptor (не-дескриптор данных): Реализует только
__get__
. Имеет более низкий приоритет, чем атрибуты вinstance.__dict__
. Пример: методы класса (@classmethod
,@staticmethod
).
Дескрипторы являются фундаментальным механизмом в Python и лежат в основе таких встроенных функций, как @property
, @classmethod
, @staticmethod
, а также используются в ORM и других фреймворках для управления атрибутами моделей.