11.18 Декораторы и дескрипторы
Декораторы
Декораторы — зачем нужны и синтаксис
проверить, что все операнды функции — это int
- Параметрические декораторы — это не страшно!
проверить, что все операнды функции — это заданный тип
- Декораторы классов
- Часто не создают новый класс, а жуют и возвращают старый
- Но можно и производный класс сделать
Задача_1:
Написать декоратор_класса objcount, который добавляет в класс поле counter (это поле класса), в котором подсчитывается количество экземпляров этого класса.
(для начала) считает количество созданных экземпляров (перегрузка .__init__())
но в конце концов надо перегрузить и .__del__(), чтобы учитывать количество созданных, но не удалённых объектов
Стоит помнить, что .__del__() у класса может и не быть
Защищать поле .counter не надо
В тестах должна проверяться корректность работы на классах с собственным __init__() и __del__()
Input:
@objcount class C: pass c, d, e = C(), C(), C() print(C.counter) c = 100500 print(C.counter)
Output:
3 2
Дескрипторы
Дескрипторы — реализация шаблона getter/setter/deleter
- Внутреннее устройство (это поле класса!)
- non-data дескрипторы (getter only)
- Где хранить то, что хранится в data дескрипторе?
Обычный «шумный» дескриптор: хранит что угодно, но сообщает о действиях
@property — удобная обмазка вокруг дескрипторов
Задача_2:
Написать дескриптор Num, который хранит только числа, а если пытаться присвоить ему последовательность, вычисляет и хранит её длину.
Числа имеют поле .real
Последовательности имеют метод .__len__()
если есть и то, и то, предпочтительнее real
- Остальные случаи не проверять
- По умолчанию значение поля типа Num = 0
Input:
class C: num = Num() print(C().num) c, d = C(), C() c.num = d.num = 2 print(c.num+d.num) c.num = "qwerqwerqwer" print(c.num+d.num) d.num = range(10, 1000, 7) print(c.num+d.num)
Output:
0 4 14 154
Слоты
Слоты — организация пространства имён экземпляра класса без __dict__
- Прозрачно для пользователя (слоты RW, поля классов RO)
- Эффективно, реализовано через дескрипторы
Задача_3:
Написать с помощью слотов класс Alpha, полем которого является любая маленькая буква латинского алфавита, и никакая другая. Класс должен поддерживать методы .__init__(), именные параметры которого задают эти поля, и .__str__(), который выводит содержимое полей объекта в алфавитном порядке:
Написать аналогичный класс AlphaQ c помощью .__getattr__() и словаря
- В тестах предусмотреть
проверку обоих классов на то, что несуществующие/незаданные поля инициируют AttributeError (так ведёт себя класс со слотами)
сравнение быстродействия обоих классов на достаточно больших объёмах чтения/записи полей (должно получаться, что Alpha быстрее, чем AlphaQ); точное время выводить не надо, только результат сравнения
Input:
alp = Alpha(c=10, z=2, a=42) alp.e = 123 print(alp) alq = AlphaQ(c=10, z=2, a=42) alq.e = 123 print(alq)
Output:
a: 42, c: 10, e: 123, z: 2 a: 42, c: 10, e: 123, z: 2