python - 如何为通用协议定义可接受类型的对或元组
问题描述
下面是一个构建调度接口的粗略示例,用于处理例如通过网络传入的消息。每条消息都包含一个带有 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
接受 , 的叉积(好像),我可以明确定义可接受的类型对吗?ConcreteMessage
MessageIdentifier
Tuple[ConcreteMessage, MessageIdentifier]
Protocol
如果您还可以确定为什么需要cast
to,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]]"
。
解决方案
推荐阅读
- python - 解决经典 P(n, r) 的 Python 代码:打印一次取 r 的 n 个对象的所有排列,不重复
- python - Python:从fasta文件中读取和打印序列
- javascript - 在 javascript 中,如何检查 textarea 是否只有一行文本?
- github - Github - 仅保留大文件的最后 n 个版本
- chef-infra - 如何使用 json 输入文件覆盖厨师属性
- javascript - Javascript通过子级递归并以HTML显示
- android - 在显示上传错误的 Play 商店中更新 React Native 应用程序时
- php - 在创建表单之前添加选项以选择 EntityType 字段的标签
- firebase - 如何从流构建器中隐藏单个项目
- swift - UIActivityViewController“保存到文件”只需要1个文件时保存多个文件