Следите за новинками
в нашем Telegram канале. Жми, чтобы подписаться:)
⊗pyOpDsInr 55 of 174 menu
◀ ▶

Введение в дескрипторы в ООП в Python

Вы уже пользовались декоратором property, а также staticmethod и classmethod. Снаружи класса такие имена ведут себя особым образом: свойство читают без круглых скобок, а статический метод можно вызвать через класс.

В Python за этим стоит один механизм - дескриптор. Это объект, который лежит в теле класса и перехватывает обращение к имени: чтение, запись или удаление.

Дескриптором считается любой объект с методами __get__, __set__ или __delete__. Их не нужно объявлять все сразу: достаточно одного, чтобы имя в классе работало через дескриптор.

Когда вы читаете свойство объекта, Python сначала ищет имя в классе. Если там лежит дескриптор, вызывается его метод __get__. Запись в свойство приведет к __set__, а удаление - к __delete__.

Встроенный property - это готовый дескриптор: он связывает геттер, сеттер и deleter в одном объекте. То же самое staticmethod и classmethod: они оборачивают функцию и сами реализуют __get__, чтобы метод можно было вызвать через класс или через объект так, как задумано.

Давайте сделаем свой простой дескриптор только для чтения. У него будет метод __get__ с тремя параметрами: self, obj и owner.

Параметр obj - это объект, через который обращаются к свойству. Параметр owner - класс, в теле которого объявлен дескриптор. Если обращение идет через класс, а не через объект, obj будет равен None. В этом случае обычно возвращают сам дескриптор.

Если obj не None, дескриптор должен вернуть значение свойства для этого объекта. Пока сохраним данные в словаре экземпляра под ключом 'name':

class NameReader: def __get__(self, obj, owner): if obj is None: return self return obj.__dict__['name'] class User: name = NameReader() def __init__(self, name): self.__dict__['name'] = name

Создадим объект и прочитаем имя как обычное свойство:

user = User('john') print(user.name) # выведет 'john'

Обратимся к имени через класс User:

print(User.name) # объект NameReader

Здесь сработало правило obj is None: мы получили сам дескриптор, а не строку. Так Python позволяет обращаться к дескриптору с уровня класса, не создавая объект.

Свой класс NameReader повторяет идею property, но в явном виде: логика чтения лежит в __get__. Методы __set__ и __delete__ устроены так же: Python вызывает их при записи и при удалении свойства.

← →
↑
Мы используем cookie для работы сайта, аналитики и персонализации. Обработка данных происходит согласно Политике конфиденциальности.
принять все настроить отклонить