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
TODO переделать — слишком похожая задача разбирается во время практикума. Вообще в этой теме не надо давать задач на сравнение производительности. Задача_3:
Написать с помощью слотов класс Alpha, полем которого является любая маленькая буква латинского алфавита, и никакая другая. Класс должен поддерживать методы .__init__(), именные параметры которого задают эти поля, и .__str__(), который выводит содержимое полей объекта в алфавитном порядке:
Написать аналогичный класс AlphaQ c помощью .__getattr__() и словаря
- В тестах предусмотреть
проверку обоих классов на то, что несуществующие/незаданные поля инициируют AttributeError (так ведёт себя класс со слотами)
сравнение быстродействия обоих классов на достаточно больших объёмах чтения/записи полей (должно получаться, что Alpha быстрее, чем AlphaQ); точное время выводить не надо, только результат сравнения
TODO более чётко описать как сравнивать время
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