You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为类型到处理函数的映射字典添加正确的Python类型提示?

问题:为类型元组映射函数的字典编写mypy兼容的类型提示

我需要创建一个字典,将输入数据类型的元组映射到对应处理该类型组合的函数,但无法写出能通过mypy检查的类型提示。以下是最小可复现代码:

from typing import Callable

class BaseType: pass
class IntType(BaseType): pass
class StringType(BaseType): pass

def _compare_int_int(before: IntType, after: IntType) -> int:
    return 1

def _compare_int_str(before: IntType, after: StringType) -> int:
    return 2

def _compare_str_int(before: StringType, after: IntType) -> int:
    return 3

def _compare_str_str(before: StringType, after: StringType) -> int:
    return 4

_COMPARERS: dict[tuple[type[BaseType], type[BaseType]], Callable[[BaseType, BaseType], int]] = {
    (IntType, IntType): _compare_int_int,
    (IntType, StringType): _compare_int_str,
    (StringType, StringType): _compare_str_str,
    (StringType, IntType): _compare_str_int
}

def compare(before: BaseType, after: BaseType) -> int:
    compare_func = _COMPARERS[(type(before), type(after))]
    return compare_func(before, after)

a = IntType()
b = StringType()

print(compare(a, a))  # 1
print(compare(a, b))  # 2
print(compare(b, a))  # 3
print(compare(b, b))  # 4

代码运行逻辑正常,但mypy抛出如下错误:

Dict entry 0 has incompatible type "Tuple[Type[IntType], Type[IntType]]": "Callable[[IntType, IntType], int]"; expected "Tuple[Type[BaseType], Type[BaseType]]": "Callable[[BaseType, BaseType], int]"

其余字典条目也会触发类似的类型不兼容错误。

我尝试过使用TypeVar来约束类型:

T = TypeVar('T', bound=BaseType)
U = TypeVar('U', bound=BaseType)

_COMPARERS: dict[tuple[type[T], type[U]], Callable[[T, U], int]] = {...}

这个写法逻辑更清晰,但仍然触发相同类型错误,求正确的类型提示写法。


解决方案

方法一:使用Protocol定义兼容签名 + 类型断言

mypy无法自动推导字典中键与值的类型对应关系,我们可以先定义一个兼容所有子类型的函数协议,再通过cast辅助类型检查:

from typing import Callable, TypeVar, cast, Protocol

class BaseType: pass
class IntType(BaseType): pass
class StringType(BaseType): pass

# 定义协议,允许接受BaseType的子类实例作为参数
class Comparer(Protocol):
    def __call__(self, before: BaseType, after: BaseType) -> int: ...

def _compare_int_int(before: IntType, after: IntType) -> int:
    return 1

def _compare_int_str(before: IntType, after: StringType) -> int:
    return 2

def _compare_str_int(before: StringType, after: IntType) -> int:
    return 3

def _compare_str_str(before: StringType, after: StringType) -> int:
    return 4

# 用协议作为字典值的类型,让所有子类型函数都能兼容
_COMPARERS: dict[tuple[type[BaseType], type[BaseType]], Comparer] = {
    (IntType, IntType): _compare_int_int,
    (IntType, StringType): _compare_int_str,
    (StringType, StringType): _compare_str_str,
    (StringType, IntType): _compare_str_int
}

T = TypeVar('T', bound=BaseType)
U = TypeVar('U', bound=BaseType)

def compare(before: T, after: U) -> int:
    # 通过cast告诉mypy,当前获取的函数可以处理T和U类型
    compare_func = cast(Callable[[T, U], int], _COMPARERS[(type(before), type(after))])
    return compare_func(before, after)

方法二:使用TypedDict枚举所有固定键值对(适用于类型组合有限的场景)

如果你的类型组合是固定的,可以用TypedDict明确列出每个键对应的精确函数类型,让mypy进行精确推导:

from typing import Callable, TypedDict, cast

class BaseType: pass
class IntType(BaseType): pass
class StringType(BaseType): pass

def _compare_int_int(before: IntType, after: IntType) -> int:
    return 1

def _compare_int_str(before: IntType, after: StringType) -> int:
    return 2

def _compare_str_int(before: StringType, after: IntType) -> int:
    return 3

def _compare_str_str(before: StringType, after: StringType) -> int:
    return 4

# 用TypedDict定义每个键对应的精确函数类型
class ComparerDict(TypedDict):
    (IntType, IntType): Callable[[IntType, IntType], int]
    (IntType, StringType): Callable[[IntType, StringType], int]
    (StringType, IntType): Callable[[StringType, IntType], int]
    (StringType, StringType): Callable[[StringType, StringType], int]

_COMPARERS: ComparerDict = {
    (IntType, IntType): _compare_int_int,
    (IntType, StringType): _compare_int_str,
    (StringType, IntType): _compare_str_int,
    (StringType, StringType): _compare_str_str
}

def compare(before: BaseType, after: BaseType) -> int:
    key = (type(before), type(after))
    assert key in _COMPARERS, f"No comparer found for types {key}"
    compare_func = _COMPARERS[key]
    # 通过cast确保函数参数与输入类型兼容
    return cast(Callable[[BaseType, BaseType], int], compare_func)(before, after)

原写法失效的原因

mypy对字典的类型检查是同构的:要求所有键都符合统一的键类型,所有值都符合统一的值类型。而Callable[[IntType, IntType], int]是Callable[[BaseType, BaseType], int]的逆变类型——函数参数更具体,无法被视为基类型函数的子类型,因此mypy判定类型不兼容。


内容的提问来源于stack exchange,提问作者TCF

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 22:44:56