查找最小值时键入 dict 提示

问题描述

以下代码,根据其键查找字典的最小值:

from typing import Dict

a: Dict[str,float] = {'a': 1,'b': 2,'c': 3}
min(a,key=a.get)

引发 mypy 错误

Value of type variable "SupportsLessthanT" of "min" cannot be "Optional[float]"

这似乎表明 Dict 中定义的类型是 Optional。如何让上面的代码通过 mypy 测试?我已尝试检查以确保没有任何值是 None,但这不起作用:

# Does not work
if any([x is None for x in a.values()]):
    raise ValueError('foo')
else:
    min(a,key=a.get)

我怎样才能用最少的代码让这段代码通过 mypy 测试?

解决方法

MyPy 不满意的原因是,作为 Mapping,字典的 get 方法在使用单个参数调用时返回值类型的 optional。例如。见the typeshed definition

class Mapping(_Collection[_KT],Generic[_KT,_VT_co]):
    # ...
    @overload
    def get(self,key: _KT) -> Optional[_VT_co]: ...
    @overload
    def get(self,key: _KT,default: Union[_VT_co,_T]) -> Union[_VT_co,_T]: ...

在没有 default 值的情况下,get 可以返回 None(如果字典中不存在该键)。在您的情况下,这永远不会发生,因为您总是使用 存在的键,但 类型 不知道这一点!

不幸的是,例如How to make type-annotation-only type assertions?,在 Python 中断言一个值的类型是很尴尬的。您可以通过将方法分配给 Any 类型的变量来有效地擦除类型:

getter: Any = a.get
min(a,key=getter)

但这似乎不太安全。另一种选择是使用调用 __getitem__ 的实现,它始终返回值类型(来自与上述相同的源的 __getitem__(self,k: _KT) -> _VT_co):

min(a,key=lambda k: a[k])