Я получаю сообщения от удаленной стороны, которые декодируются в классы, которые выглядят следующим образом:
class SomeMessage(MessageType):
foo: Optional[int]
bar: Optional[str]
quux: Optional[AnotherMessageType]
Большинство полей всегда являются необязательными. Телеграфное сообщение может содержать их, а может и не содержать.
Однако мой код выполняет проверку заранее, а затем в основном предполагает наличие соответствующих полей.
E.g.:
def process_all_foos(messages: List[SomeMessage]):
messages_with_foo = [m for m in messages if m.foo is not None]
foo_sum = sum(m.foo for m in messages_with_foo)
Этот код не проходит проверку типов, потому что mypy не выполняет предыдущую проверку.
Есть ли возможность каким-то образом выполнить проверку типов приведенного выше кода без затрат времени выполнения?
Моя идея заключалась в создании протокола с более строгим требованием:
class SomeMessageWithFoo(Protocol):
foo: int
def convert_foo(m: SomeMessage) -> SomeMessageWithFoo:
assert m.foo is not None
return m
# or, ideally:
def process_all_foos(messages: List[SomeMessage]):
messages_with_foo: List[SomeMessageWithFoo] = [m for m in messages if m.foo is not None]
foo_sum = sum(m.foo for m in messages_with_foo)
Однако mypy отвергает это:
test.py:16: error: Incompatible return value type (got "SomeMessage", expected "SomeMessageWithFoo") [return-value]
test.py:16: note: Following member(s) of "SomeMessage" have conflicts:
test.py:16: note: foo: expected "int", got "Optional[int]"
test.py:20: error: List comprehension has incompatible type List[SomeMessage]; expected List[SomeMessageWithFoo] [misc]
test.py:20: note: Following member(s) of "SomeMessage" have conflicts:
test.py:20: note: foo: expected "int", got "Optional[int]"
Я рассматривал возможность использования cast(SomeMessageWithFoo, m)
или, что более вероятно, # type: ignore
, потому что это не требует затрат времени выполнения. (Я работаю во встроенной среде, поэтому меня волнует вызов функции, который делает cast
.) Но это также позволяет мне не проверять поле.
То есть, следующие неправильные проверки типов:
def convert_foo(m: SomeMessage) -> SomeMessageWithFoo:
# someone commented this out:
#assert m.foo is not None
return cast(SomeMessageWithFoo, m)
Есть ли способ сделать это, который выполняет проверку типа кода, но при этом фактически проверяет требования?