首页 > 解决方案 > 如何为通用协议定义可接受类型的对或元组

问题描述

下面是一个构建调度接口的粗略示例,用于处理例如通过网络传入的消息。每条消息都包含一个带有 MessageIdentifier 的标头,并且应用程序能够为相应的已解析 ConcreteMessage 类型定义各种回调。

调度的关键是定义 MessageIdentifiers 和 ConcreteMessages 之间的关联,定义handle_message如下。在运行时,这是类型安全的,因为不可能使用除适当类型之外的任何内容调用回调。但我想知道是否可以限制 mypy 允许的标识符和具体消息对,理想情况下,当使用不正确的类型调用回调时显示错误。

from enum import Enum
from typing import Callable, Optional, TypeVar, cast
from typing_extensions import Literal, Protocol

class MessageType(Enum):
    FOO = 0
    BAR = 1
    BAZ = 2

class FooMessage: ...
class BarMessage: ...
class BazMessage: ...

ConcreteMessage = TypeVar("ConcreteMessage", FooMessage, BarMessage, BazMessage)
MessageIdentifier = TypeVar("MessageIdentifier", Literal[MessageType.FOO], Literal[MessageType.BAR], Literal[MessageType.BAZ])

class MessageHandler(Protocol):
    def __getitem__(self, item: MessageIdentifier) -> Callable[[ConcreteMessage], Optional[bool]]: ...

def handle_foo(foo: FooMessage) -> Optional[bool]: ...
def handle_bar(bar: BarMessage) -> Optional[bool]: ...

handle_message = cast(MessageHandler, {
    MessageType.FOO: handle_foo,
    MessageType.BAR: handle_bar,
})

# MessageIdentifier constrains correctly
func = handle_message[5]                # error: Value of type variable "MessageIdentifier" of "__getitem__" of "MessageHandler" cannot be "Literal[5]"
func = handle_message[MessageType.FOO]  # good
# ConcreteMessage constrains correctly
func(FooMessage())  # good
func(5)             # error: Value of type variable "ConcreteMessage" of function cannot be "int"
func(BarMessage())  # good - but would ideally be an error

换一种说法,除了MessageHandler接受 , 的叉(好像),我可以明确定义可接受的类型对吗?ConcreteMessageMessageIdentifierTuple[ConcreteMessage, MessageIdentifier]Protocol


如果您还可以确定为什么需要castto,MessageHandler则可以加分。当定义如下:

handle_message: MessageHandler = {
    MessageType.FOO: handle_foo,
    MessageType.BAR: handle_bar,
}

mypy 报告Dict entry 0 has incompatible type "Literal[MessageType.FOO]": "Callable[[FooMessage], Optional[bool]]"; expected "MessageIdentifier": "Callable[[ConcreteMessage], Optional[bool]]"

标签: pythongenericstype-hintingmypy

解决方案


推荐阅读