12.03 Метаклассы, сопоставление шаблону и аннотации
Метаклассы
- Зачем (не) нужны
Создание класса с помощью type(имя, родители, атрибуты)
Создать класс C, который работал бы так:
Если получится — в одну строку (__init__ задать лямбдой + setattr())
«Шумный» метаклаcc, в котором переопределены 4 метода
Упростить final из лекции: написать метакласс sole, запрещающий классу иметь более одного непосредственного родителя
1 >>> class C(metaclass=sole): 2 ... pass 3 ... class D(C): pass 4 >>> class E(C, int): pass 5 Traceback (most recent call last): 6 File "<stdin>", line 1, in <module> 7 File "<stdin>", line 4, in __new__ 8 TypeError: Cannot have more tan one parent 9 10 >>> class E(D, int): pass 11 Traceback (most recent call last): 12 File "<stdin>", line 1, in <module> 13 File "<stdin>", line 4, in __new__ 14 TypeError: Cannot have more tan one parent
На основании Singleton из лекции написать метакласс Doubleton. Экземпляров класса, созданных с его помощью, должно быть не больше двух, каждое следующее создание экземпляра должно возвращать по очереди либо один, либо второй.
1 >>> class C(metaclass=Doubeleton): pass 2 >>> print(*(C() for i in range(7)), sep="\n") 3 <__main__.C object at 0x7fe22f976910> 4 <__main__.C object at 0x7fe22ea57990> 5 <__main__.C object at 0x7fe22f976910> 6 <__main__.C object at 0x7fe22ea57990> 7 <__main__.C object at 0x7fe22f976910> 8 <__main__.C object at 0x7fe22ea57990> 9 <__main__.C object at 0x7fe22f976910> 10
Сопоставление шаблону
Ещё раз: синтаксис case — это не синтаксис всего остального питона!
Общий вид и связанные переменные; распаковка
При помощи конструкции match/case напишите "интерпретатор ассемблера", распознающий три вида команд:
mov <r1> <r2>, где <r1> и <r2> — строки без пробелов, задающие имена регистров. Пример: mov ax bx. Для такой команды выводить: "moving <r2> to <r1>".
push <reglist> и pop <reglist>, где <reglist> это перечень имен регистров через пробел. Пример: pop esr1 esr2 esr112. Для такой команды выводить: "<cmd>ing <reglist>", например "pushing esr1 esr2 esr112".
hint: используйте конструкцию "as <переменная>"
<cmd> <r1>, где <cmd> и <r1> — строки без пробелов. Для такой команды выводить "making <cmd> with <r1>"
- для входных строк других форматов выводить "Parse error"
Связанные переменные vs константы, инкапсуляция констант. Фильтры.
Ввести строку, состоящую из трёх слов, разделённых пробелами. Далее вводить строки (из слов, разделённых пробелами) и с помощью match проверять три случая:
Строка начинается на первое слово, и в ней есть слово "yes"
- Тут надо применить фильтр
- Строка — это второе слово
- Строка начинается на третье, а заканчивается на второе слово
Аннотации
- Зачем (не) нужны
Засада с текстовыми аннотациями (например, в PtPython)
Написать простой аннотированный класс и посмотреть в нём аннотации
inspect.get_annotations()
Написать класс, в котором есть аннотированное поле a некоторого типа и метод __init__(val) заполняет это поле своим единственным параметром, проверяя, что тип этого параметра совпадает с указанным в аннотации.
Д/З
Задача_1. Написать метакласс dump, который "обмазывает" все имеющиеся в словаре методы класса выводом имени метода и значений его параметров (*args, **kwargs)
через параметры методов передаются только значения типов str, int, float и bool (и никаких других типов)
методы задаются только классическим путём (def метод()…)
- не используются декораторы методов (например, нет статических методов и методов класса)
формат вывода: имя_метода: кортеж_args, словарь_kwargs
- вывод должен осуществляться при каждом вызове метода
Input:
Output
__init__: (10,), {} add: (9,), {} 29 add: (9,), {'another': 10} 29
Задача_2. «Ассемблер». Написать интерпретатор ассемблероподобных команд, обрабатывающих вещественные числа.
- Программа на «ассемблере» состоит из команд или некоманд, по одной команде (или некоманде) в строке
- Команда состоит из операции и параметров, разделённых ненулевым количеством пробелов. Пробельные символы в начале строки игнорируются.
- Параметры бывают трёх видов: вещественные числа, имена переменных и имена меток.
- В начале любой строки, включая содержащую некоманды, может стоять метка — строка, заканчивающаяся на «":"».
Имена и метки могут состоять из любых непробельных символов, кроме «:» (этот факт тоже проверять ненадо)
Команды (во всех командах источник, операнд и приёмник — это переменные):
stop — выход из программы. Также выход из программы происходит при попытке выполнить команду, идущую после последней
store число приёмник — запись числа в приёмник. Переменные объявлять не надо. По умолчанию переменные равны 0. Если число нельзя интерпретировать как вещественное число, оно считается равным 0.
OP иcточник операнд приёмник — выполнение арифметической операции над источником и операндом и запись результата в приёмник. OP — это add, sub, div и mul. Если при вычислении возникает ошибка (например, деление на 0), операция возвращает math.inf
CMP иcточник операнд метка — сравнение источника с операндом; если сравнение истинно, переход по метке метка. CMP — это ifeq, ifne, ifgt, ifge, iflt или ifle, то есть ==, !=, >, >=, < или <= соответственно.
out источник — вывод переменной на стандартный вывод
- Все некоманды (в том числе команды с неправильным числом параметров) игнорируются
- Если в программе присутствует переход на несуществующую метку, интерпретатор не начинает работу, не выводит и не делает ничего
- Подсказка: проще всего решать задачу в два прохода: сначала составить список одних только команд, заодно проверив, что все метки определены, а потом уже интерпретировать
Интерпретатор интерпретирует команды классическим способом — по очереди (с учётом переходов) до тех пор, пока не встретит команду "stop" или пока не попытается выполнить несуществующую команду после последней
Input:
Эта программа вычисляет e store 100 i store 1 I store 1 f Переменные e и n коварно не объявляем — в них по умолчанию 0 loop: div I f a add e a e add n I n mul f n f sub i I i ifge i I loop out e
Output
2.7182818284590455
Во всех текстах к задаче должна присутствовать команда if…. Один из тестов должен содержать неопределённую метку (вывод при этом пуст).
(необязательно) чисто из спортивного интереса в моём решении нет ни одного if ☺