Python: обмен аннотациями типов между протоколом и TypedDict - PullRequest
2 голосов
/ 22 февраля 2020

Возьмем этот простой пример:

from __future__ import annotations
import typing as t


class MyType:
    def __init__(self, s: str, i: int) -> None:
        self.s = s
        self.i = i


class MyProto(t.Protocol):
    s: str
    i: int


class MyDict(t.TypedDict):
    s: str
    i: int


def my_serializer(inst: MyProto) -> MyDict:
    return {"s": inst.s, "i": inst.i}


d = my_serializer(MyType("a", 1))

Все проверки типов пройдены.

Теперь давайте скажем, что MyType на самом деле класс ORM со многими атрибутами, который является источником истины для набор протокола и дикт. Чувствуется немного избыточным необходимость поддерживать идентичные аннотации в теле класса Protocol и теле класса TypedDict каждый раз, когда атрибут добавляется в класс.

Я хотел бы знать, есть ли способ, которым я может централизованно определять аннотации типов и сообщать mypy, что это определения типов как для протокола, так и для класса dict.

Я пробовал это:

class TypeMixin:
    s: str
    i: int


class MyProto(TypeMixin, t.Protocol):
    pass


class MyDict(TypeMixin, t.TypedDict):
    pass

Однако mypy жалуется:

test.py:15: error: All bases of a protocol must be protocols
test.py:19: error: All bases of a new TypedDict must be TypedDict types

... и это на самом деле ошибка TypeError во время выполнения.

И это:

annos = {"s": "str", "i": "int"}
MyProto = type("MyProto", (t.Protocol,), {"__annotations__": annos})
MyDict = type("MyDict", (t.TypedDict,), {"__annotations__": annos})


def my_serializer(inst: MyProto) -> MyDict:
    return {"s": inst.s, "i": inst.i}

Это работает, но Mypy жалуется, и я предполагаю, что это все слишком динамично c для mypy в любом случае:

test.py:12: error: Argument 2 to "type" has incompatible type "Tuple[_SpecialForm]"; expected "Tuple[type, ...]"
test.py:13: error: Argument 2 to "type" has incompatible type "Tuple[object]"; expected "Tuple[type, ...]"
test.py:16: error: Variable "topsport.events.test.MyProto" is not valid as a type
test.py:16: error: Variable "topsport.events.test.MyDict" is not valid as a type
test.py:17: error: MyProto? has no attribute "s"
test.py:17: error: MyProto? has no attribute "i"

Разве то, что я пытаюсь сделать, невозможно?

...