Дескрипторы — это мощный механизм управления доступом к атрибутам объектов, который лежит в основе многих встроенных возможностей Python, таких как свойства (property), методы классов (classmethod, staticmethod) и слоты (__slots__).
Дескриптор — это любой объект, который реализует хотя бы один из трех специальных методов:
__get__(self, obj, type=None) — для получения значения__set__(self, obj, value) — для установки значения__delete__(self, obj) — для удаления атрибутаРеализует __set__ и/или __delete__ в дополнение к __get__
class DataDescriptor:
def __get__(self, obj, objtype=None):
print("Получение значения")
return obj._value
def __set__(self, obj, value):
print("Установка значения")
obj._value = value
class MyClass:
attr = DataDescriptor() # Дескриптор данных
Реализует только __get__
class NonDataDescriptor:
def __get__(self, obj, objtype=None):
print("Получение значения (non-data)")
return 42
class MyClass:
attr = NonDataDescriptor() # Не-дескриптор данных
class ValidatedAttribute:
def __init__(self, name, type_, default=None):
self.name = name
self.type = type_
self.default = default
def __get__(self, obj, objtype):
if obj is None:
return self
return getattr(obj, f"_{self.name}", self.default)
def __set__(self, obj, value):
if not isinstance(value, self.type):
raise TypeError(f"Ожидается {self.type}, получено {type(value)}")
setattr(obj, f"_{self.name}", value)
class Person:
name = ValidatedAttribute("name", str)
age = ValidatedAttribute("age", int, 0)
# Использование
p = Person()
p.name = "Иван" # OK
p.age = 30 # OK
p.age = "30" # Вызовет TypeError
При доступе к атрибуту Python следует алгоритму:
__dict__ экземпляра__getattr__ (если определен)property — самый распространенный дескрипторclass Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value < 0:
raise ValueError("Радиус не может быть отрицательным")
self._radius = value
classmethod и staticmethod также реализованы через дескрипторыobj is None в __get__property, classmethod, staticmethodДескрипторы — это продвинутая, но очень мощная возможность Python, которую стоит освоить для написания идиоматического и поддерживаемого кода.