11.29 Метаклассы и аннотации
FIXME: "вроде просто" сформулированные упражненьки на метаклассы/аннотации оказываются сложными из-за нагрузки на голову, ибо сам материал сложный.
FIXME: Пока просто публикуем планируемые результаты упражнений, к следующему году надо переделать: забить на mypy и вставить больше демо
FIXME: Просмотреть на предмет достаточности примеров для решения Д/З
Метаклассы
- Зачем (не) нужны
 Создание класса с помощью type(имя, родители, атрибуты) Создание класса с помощью type(имя, родители, атрибуты)
 Создать класс C, который работал бы так: Создать класс C, который работал бы так:- Если получится — в одну строку (__init__ задать лямбдой + setattr()) 
 
 «Шумный» метаклаcc, в котором переопределены 4 метода «Шумный» метаклаcc, в котором переопределены 4 метода
 Написать метакласс Both, при создании класса с помощью которого можно передавать два именных параметра: final= и single=. Если соответствующий параметр непуст, порождаемый класс не может выступать в роли предка и/или является синглтоном Написать метакласс Both, при создании класса с помощью которого можно передавать два именных параметра: final= и single=. Если соответствующий параметр непуст, порождаемый класс не может выступать в роли предка и/или является синглтоном- FIXME: упражненька тяжеловата оказалась 1 class Both(type): 2 _instance = None 3 4 def __call__(cls, *args, **kw): 5 if not cls._single or cls._instance is None: 6 cls._instance = super().__call__(*args, **kw) 7 return cls._instance 8 9 def __new__(metacls, name, parents, namespace, **flags): 10 cls = super().__new__(metacls, name, parents, namespace) 11 cls._single = flags.get("single", False) 12 cls._final = flags.get("final", False) 13 for cls in parents: 14 if isinstance(cls, Both) and cls._final: 15 raise TypeError(f"{cls.__name__} is final") 16 return cls 
 
- FIXME: упражненька тяжеловата оказалась 
Аннотации
- зачем (не) нужны)
 Написать простой аннотированный класс и посмотреть в нём аннотации Написать простой аннотированный класс и посмотреть в нём аннотации
 как из кода добраться до аннотации функции (метода класса) как из кода добраться до аннотации функции (метода класса)
 TODO пока публикуем результат. Написать класс, в котором __init__() , проверяет с помощью isinstance() тип единственного передаваемого параметра согласно аннотации; иначе — TypeError() TODO пока публикуем результат. Написать класс, в котором __init__() , проверяет с помощью isinstance() тип единственного передаваемого параметра согласно аннотации; иначе — TypeError()
TODO На этом всё застряло
 Аннотации составных типов данных (list[int]) Аннотации составных типов данных (list[int])
 Модифицировать предыдущее упражнение так, чтобы __init__() допускал в качестве параметра только итерируемую последовательность данных (как минимум, список или кортеж) определённого типа. Тип последовательности и тип данных задаётся аннотацией Модифицировать предыдущее упражнение так, чтобы __init__() допускал в качестве параметра только итерируемую последовательность данных (как минимум, список или кортеж) определённого типа. Тип последовательности и тип данных задаётся аннотацией
mypy/mypyc
 Установка и использование mypy Установка и использование mypy
 Написать функцию, которая вычисляет число Пи по формуле лейбница, добиться её работоспособности и отсутствия ошибок mypy --strict Написать функцию, которая вычисляет число Пи по формуле лейбница, добиться её работоспособности и отсутствия ошибок mypy --strict
 Использование mypyc Использование mypyc
 Сравнение производительности скомпилированной и интерпретируемой программы Сравнение производительности скомпилированной и интерпретируемой программы- Возьмите пример с рекурсивной сортировкой слиянием, поместите его в файл merge.py: - 1 import random 2 3 def merge(sp1, sp2): 4 result = [] 5 while sp1 and sp2: 6 if sp1[0] < sp2[0]: 7 h, *sp1 = sp1 8 else: 9 h, *sp2 = sp2 10 result.append(h) 11 result.extend(sp1) 12 result.extend(sp2) 13 return result 14 15 def merge_sort(sp): 16 if len(sp) <= 1: 17 return sp 18 m = len(sp)//2 19 sp1, sp2 = merge_sort(sp[:m]), merge_sort(sp[m:]) 20 return merge(sp1, sp2) 
- запустите пример в интерпретаторе Python, затем скомпилируйте с помощью mypyc и запустите скомпилированный пример. 
- сравните выведенные при запусках длительности выполнения
 
Д/З
Напомнить людям про перекрёстное тестирование
 Задача_1. Написать метакласс dump, который "обмазывает" все имеющиеся в словаре методы класса выводом имени метода и значений его параметров (*args, **kwargs) Задача_1. Написать метакласс dump, который "обмазывает" все имеющиеся в словаре методы класса выводом имени метода и значений его параметров (*args, **kwargs)- через параметры методов передаются только значения типов str, int, float и bool (и никаких других типов) 
- методы задаются только классическим путём (def метод()…) 
- не используются декораторы методов (например, нет статических методов и методов класса)
- формат вывода: имя_метода: кортеж_args, словарь_kwargs 
- вывод должен осуществляться при каждом вызове метода
- Пример: - →
 __init__: (10,), {} add: (9,), {} 29 add: (9,), {'another': 10} 29
 
 Задача_2. Написать метакласс check, который добавляет в создаваемый класс метод check_annotations(), возвращающий True, если типы всех аннотированных полей объекта (а) существуют и (б) соответствуют аннотации, и False в противном случае Задача_2. Написать метакласс check, который добавляет в создаваемый класс метод check_annotations(), возвращающий True, если типы всех аннотированных полей объекта (а) существуют и (б) соответствуют аннотации, и False в противном случае- Если поле имеет составной тип, например list[int], проверять только соответствие типу list - Подсказка: такая конструкция имеет тип types.GenericAlias, а тип объекта хранится в поле .__origin__ 
 
- Если ни сама аннотация, ни тип, хранящийся в дженерике не совпадают ни с типом объекта, проверка соответствия неуспешна
- Методы проверять не надо
- Пример: 1 class C(metaclass=check): 2 A: list[int] 3 B: str = "QQ" 4 5 c = C() 6 print(c.check_annotations()) 7 c.A = "ZZ" 8 print(c.check_annotations()) 9 c.A = [100500, 42, 0] 10 print(c.check_annotations()) 11 c.B = type("Boo",(str,),{})(42) 12 print(c.check_annotations()) 13 c.A = ["FALSE"] 14 print(c.check_annotations()) - →
 False False True True True 
 
