11.18 Декораторы и дескрипторы
Декораторы
Декоратор с выводом параметров функции
Написать декоратор isint, который добавляет проверку, что все операнды функции — это int, иначе формирует исключение TypeError
Параметрические декораторы
написать декоратор istype(typ), который добавляет проверку, что все операнды функции — это заданный тип typ (использовать isinstance()),
Класс как декоратор
оформить предыдущее задание (про istype(typ)) в виде класса
Применение @staticmethod / @classmethod:
Напишите класс Sender, у которого есть метод_класса report(), выводящий при первом за всё выполнение программы вызове "Greetings!", а при втором и последующем "Get away!"
- используйте как признак "первости" поле класса, имеющее булев тип
Напишите класс Asker со статическим методом askall(lst), для каждого элемента списка lst вызывающим метод report()
Проверьте работу Asker.askall() у экземпляра класса Asker на списке из трех экземпляров класса Sender
Дескрипторы
Дескрипторы — реализация шаблона getter/setter/deleter
Обычный «шумный» дескриптор: хранит что угодно, но сообщает о действиях
- Проблема хранения данных
Переписать одну из старых задач — счётчик экземпляров — на дескрипторы. Написать дескриптор Counter таим образом, чтобы работало следующее:
- FIXME: во врезке выше перемешаны команды и их вывод. Переделать врезку, есть примеры где этой проблемы нет.
Подсказка: проблемы хранения данных нет, наоборот, удобно, что counter — поле класса
@property — обмазка вокруг дескрипторов
Применение @property. Напишите класс C со свойством age, для которого определены:
setter — если переданное значение не больше 128, присваивает его, иначе выводит "too old!" и формирует исключение ValueError
- getter — если значение равно 42, выводит "secret value!" и возвращает -1, иначе возвращает само значение
TODO успеем ли? .__set_name__? (2024 г. - не успели, но начало занятия задержалось)
Слоты
Слоты — организация пространства имён экземпляра класса без __dict__
Прозрачно для пользователя (слоты RW, поля классов RO)
Сравнение класса традиционного и класса со слотами по использованию памяти.
создайте класс Trad:
Создайте такой же класс Slotter, дописав ему в начало __slots__ из того же ascii_letters
породите экземпляры t и s этих (соответственно) классов
импортируйте модуль sys (import sys); при помощи sys.getsizeof() узнайте размеры t и s
o_O Что-то тут не то!
pip install pympler
from pympler.asizeof import asizeof и asizeof чего попало, например, списка
Создать два списка по 1000 экземпляров каждого класса (Trad и Slotter) и сравнить их размеры
from pympler import asizeof и asizeof.asizeof(список)
Сравнение класса традиционного и класса со слотами по производительности (источник: https://stackoverflow.com/questions/472000/usage-of-slots).
возьмите классы Trad, Slotter из предыдущего упражнения
- подготовьте окружение для сравнения производительности:
сравните быстродействие операций с экземплярами R и S:
Д/З
Задача_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:
Написать с помощью слотов класс Vowel, полем которого является любая маленькая гласная буква латинского алфавита («aouiey»), и никакая другая. Класс должен поддерживать методы .__init__(), именные параметры которого задают эти поля (возможно, не все, или никакие вообще), и .__str__(), который выводит содержимое полей объекта в алфавитном порядке. Класс также должен содержать поле только для чтения — answer, всегда равное 42, и поле full, которое принимает значение True, только если всем слотам объекта присвоено значение, иначе False; запись в это поле не должна иметь никакого эффекта.
- В тестах (помимо указанного в примере) предусмотреть
проверку на исключение при попытке записи в answer
- проверку на исключение при попытке чтения из незаполненного слота
- проверку на исключение при попытке чтения и записи не в слот
проверку безрезультатности записи в full
Input:
wo = Vowel(y=22, a=12, i=3) print(wo, wo.full) wo.e = wo.o = wo.u = 100500 print(wo, wo.full)
Output:
a: 12, i: 3, y: 22 False a: 12, e: 100500, i: 3, o: 100500, u: 100500, y: 22 True