11.15 Декораторы и дескрипторы
Декораторы
Декоратор с выводом параметров функции
проверить, что все операнды функции — это int, иначе формировать исключение TypeError
Параметрические декораторы
проверить, что все операнды функции — это заданный тип (использовать isinstance()),
Декораторы классов
Применение @staticmethod / @classmethod:
Напишите класс Asker со статическим методом askall(lst), для каждого элемента списка lst вызывающим метод report()
Напишите класс Sender, у которого есть метод_класса report(), выводящий при первом за всё выполнение программы вызове "Greetings!", а при втором и последующем "Get away!" (используйте как признак "первости" поле класса, имеющее булев тип)
Проверьте работу Asker.askall() у экземпляра класса Asker на списке из трех экземпляров класса Sender
Дескрипторы
Дескрипторы — реализация шаблона getter/setter/deleter
TODO Обычный «шумный» дескриптор: хранит что угодно, но сообщает о действиях
Где хранить то, что хранится в data дескрипторе?
Деконструкция дескриптора.
напишите (как в демонстрации) "шумный" дескриптор C
напишите класс FOO с полем x типа C
создайте экземпляр foo класса FOO; проверьте на нём, что дескриптор сообщает о действиях в ответ на присваивание полю x, обращение к полю x, удаление поля x
удалите метод .__set__() из класса C, посмотрите как foo реагирует на запись в поле x; аналогично для .__get__() и чтения x, .__delete__() и удаления x
@property — удобная обмазка вокруг дескрипторов
Применение @property. Напишите класс C со свойством age, для которого определены:
setter — если переданное значение не больше 128, присваивает его, иначе выводит "too old!" и формирует исключение ValueError
- getter — если значение равно 42, выводит "secret value!" и возвращает -1, иначе возвращает само значение
Слоты
Слоты — организация пространства имён экземпляра класса без __dict__
- Прозрачно для пользователя (слоты RW, поля классов RO)
- Эффективно, реализовано через дескрипторы
Сравнение класса традиционного и класса со слотами по использованию памяти.
создайте класс R с полем x и класс S со слотом x
породите экземпляры r и s этих (соответственно) классов, присвойте полям r и s значение 0
импортируйте модуль sys (import sys); при помощи sys.getsizeof() узнайте размеры r и s
Сравнение класса традиционного и класса со слотами по производительности (источник: https://habr.com/ru/post/686220/).
возьмите классы R, S из предыдущего упражнения
- подготовьте окружение для сравнения производительности:
сравните быстродействие операций с экземплярами R и S:
Д/З
TODO вычитать
Задача_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
Задача_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
Задача_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