Написать метакласс checked, при использовании которого в порождаемом классе проверяются соответствия типов всех аннотированных параметров и возвращаемых значений всех методов. В случае несовпадения инициируется TypeError: Type mismatch: <параметр>
- Значения параметров по умолчанию не проверяются (для простоты)
- Поля не проверяются, только методы
Предполагается, что должна быть верна проверка isinstance(объект, тип), в противном случае соответствие считается неуспешным
При вызове (по крайней мере, в тестах) не используются конструкции *args и **kwargs
- Параметры проверяются (если они аннотированы, конечно) в следующем порядке
- Позиционные (переданные в кортеже и распакованные) параметры, по порядку
- Явно заданные именные (переданные в словаре и распакованные) параметры в порядке вызова метода
Нераспакованные позиционные параметры, полученные через *args. В этом случае в строке исключения пишется args (имя, которое при описании функции принимало запакованные позиционные параметры)
Нераспакованные именные параметры, полученные через **kwargs
Возвращаемое значение (имя параметра "return", как в аннотации)
1 class E(metaclass=checked):
2 def __init__(self, var: int):
3 self.var = var if var%2 else str(var)
4
5 def mix(self, val: int, opt) -> int:
6 return self.var*val + opt
7
8 def al(self, c: int, d:int=1, *e:int, f:int=1, **g:int):
9 return self.var*d
10
11 e1, e2 = E(1), E(2)
12 code = """
13 e1.mix("q", "q")
14 e1.mix(2, 3)
15 e2.mix(2, "3")
16 e1.al("q")
17 e1.al(1, 2, 3, 4, 5, 6, foo=7, bar=8)
18 e2.al(1, 2, 3, 4, 5, 6, foo=7, bar=8)
19 e1.al("E", 2, 3, 4, 5, 6, foo=7, bar=8)
20 e1.al(1, "E", 3, 4, 5, 6, foo=7, bar=8)
21 e1.al(1, 2, "E", 4, 5, 6, foo=7, bar=8)
22 e1.al(1, 2, 3, "E", 5, 6, foo="7", bar=8)
23 e1.al(1, f="E", d=1)
24 e1.al(1, f=1, d="E")
25 e1.al(1, f="E", d="1")
26 e1.al(1, d="E", f="1")
27 e1.al(1, e="E")
28 e1.al(1, g="E")
29 """
30
31 for c in code.strip().split("\n"):
32 try:
33 res = eval(c)
34 except TypeError as E:
35 res = E
36 print(f"Run: {c}\nGot: {res}")
Спойлер исследования:
Спойлер чтения документации:
Run: e1.mix("q", "q") Got: Type mismatch: val Run: e1.mix(2, 3) Got: 5 Run: e2.mix(2, "3") Got: Type mismatch: return Run: e1.al("q") Got: Type mismatch: c Run: e1.al(1, 2, 3, 4, 5, 6, foo=7, bar=8) Got: 2 Run: e2.al(1, 2, 3, 4, 5, 6, foo=7, bar=8) Got: 22 Run: e1.al("E", 2, 3, 4, 5, 6, foo=7, bar=8) Got: Type mismatch: c Run: e1.al(1, "E", 3, 4, 5, 6, foo=7, bar=8) Got: Type mismatch: d Run: e1.al(1, 2, "E", 4, 5, 6, foo=7, bar=8) Got: Type mismatch: e Run: e1.al(1, 2, 3, "E", 5, 6, foo="7", bar=8) Got: Type mismatch: e Run: e1.al(1, f="E", d=1) Got: Type mismatch: f Run: e1.al(1, f=1, d="E") Got: Type mismatch: d Run: e1.al(1, f="E", d="1") Got: Type mismatch: f Run: e1.al(1, d="E", f="1") Got: Type mismatch: d Run: e1.al(1, e="E") Got: Type mismatch: e Run: e1.al(1, g="E") Got: Type mismatch: g