如何定义一个字典,将类型映射到接收该类型参数的可调用对象?

2 投票
1 回答
60 浏览
提问于 2025-04-14 18:30

在Python中,我在定义一个类型时遇到了困难。我想要一个类似这样的东西:

def foo(x: int) -> int:
    return x
    
def bar(x: str) -> str:
    return x

# [...] MyDictType would be defined here...
        
my_dict: MyDictType = {
    int: foo,       # Good
    str: bar,       # Good
    float: bar      # WRONG! Type warning!
}

MyDictType应该表示这是一个字典,它将某种类型映射到一个可以接收该类型变量的可调用对象。我在想如何使用泛型来实现这个...但我不太确定。我试过这样做:

T = TypeVar("T")

MyDictType = dict[Type[T], Callable[[T], T]]

然而,编辑器并没有显示我期待在字典第三个条目中看到的错误...

我应该如何定义MyDictType呢?

1 个回答

1

这个事情是没办法做到的。没有任何注解可以表达单个字典键和它们对应值之间的关系。

你能做到的最接近的方式是定义一个包装器:

from collections.abc import Callable
from typing import Any, TypeVar

T = TypeVar('T')

class TypeMapperThing:
    _mapping: dict[type, Callable[[Any], Any]]
    def __init__(self):
        self._mapping = {}
    def __getitem__(self, key: type[T]) -> Callable[[T], T]:
        return self._mapping[key]
    def __setitem__(self, key: type[T], value: Callable[[T], T]) -> None:
        self._mapping[key] = value
    def __delitem__(self, key: type[T]) -> None:
        del self[key]

在这里,这个包装器隐藏了使用 Any 的部分,并提供了一个类型安全的外部接口。(我本可以省略 Callable 的类型参数,直接写 _mapping: dict[type, Callable],但那样会用到 Callable[..., Any],而不是 Callable[[Any], Any]。不过这其实没什么大不了的。)

需要注意的是,没有好的方法让 __init__ 接受一个字典,或者写一个接受字典的 update 方法。尝试这样做又会遇到最初的问题,就是如何为那个字典写一个类型。

我也没有使用 collections.abc.Mappingcollections.abc.MutableMapping,因为这些抽象基类定义的接口也会遇到和 dict 类似的问题。

撰写回答