问题描述
以下代码,根据其键查找字典的最小值:
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 不满意的原因是,作为 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])