我正在研究typing.pyi
file,以更好地了解通用容器类型提示的工作原理,并在MutableMapping
类定义中找到了以下几行:
@overload
def pop(self, k: _KT) -> _VT: ...
@overload
def pop(self, k: _KT, default: Union[_VT, _T] = ...) -> Union[_VT, _T]: ...
我不理解的部分是_T
附加参数。只能为MutableMapping
指定两个参数:_KT
和_VT
。因此,此额外的_T
参数仍未指定。类型检查器如何解决第三种类型...?
答案 0 :(得分:1)
我本来打算在评论中留这个,但是我的回答开始变得有点长,所以这里是...
只能为MutableMapping指定两个参数:_KT和_VT。因此,此额外的_T参数仍未指定。类型检查器如何解决第三种类型...?
正如@jonrsharpe所说,_T
不是是类类型参数,它是函数类型参数。基本上,当您调用该方法时,mypy将具有:
_KT
和_VT
绑定到其他某种类型。因此,如果我们执行foo: MutableMapping[str, int] = ...
然后调用foo.pop(...)
,mypy将理解_KT
和_VT
分别在str
和int
处绑定。我们打电话给pop
的时间。 Mypy还会注意到,如果我们调用第二个重载,我们会有一个 free (又名 unbound )类型参数浮动。然后它将尝试推断根据我们传入的/周围环境的值,_T
的正确类型是什么。
例如,假设我做foo.pop("x", "bar")
。这与第二个过载匹配。我们之前也曾说过_KT
和_VT
分别绑定到str
和int
。然后Mypy注意到_T
是未绑定的,并尝试推断适当的类型。
在这里,我们知道我们要传递的值的类型为“ str”,参数类型为Union[int, _T]
(在替换绑定类型之后)。我们也知道我们传入的值必须是参数的子类型-我们知道str
必须是Union[int, _T]
的子类型。
然后Mypy使用上述所有信息/所有已知约束条件运行推理算法,并能够在这种情况下推断_T
的类型必须为str
。
(作为一个附带说明,mypy的推断算法并不完美。如果表达式特别复杂/当前不能正确处理某些边缘情况,有时可能无法推断正确的类型。)
如果您想更详细地描述mypy的类型推断算法的工作原理,可以尝试通过mypy的代码库进行拼写。具体而言,mypy调用this function,而后者依次调用code in here。合理的警告,有点难以理解。
在这种情况下,_T返回类型是协变量吗?
我们在这里知道_KT
,_VT
和_T
都是基于their definition不变的。
这三个类型变量中的每一个都可以完全独立。这符合运行时的行为:如果我有一个Dict[str, str]
,则根据my_dict.pop("x", 4)
所包含的内容,执行my_dict
可能会返回一些字符串或数字4。
返回类型必须与默认类型相同还是仅是兼容的子类型?
因此,完全搁置泛型,这是mypy 0.620以后的(简化)重载规则。 (旧版本的mypy使用了类似但更蓬松的即席算法)。
Mypy将禁止本来不安全的重载定义。在以下情况下,有两种过载变体固有地被认为是不安全的:
在pop
的特定情况下,第一个变量的参数确实与第二个变量兼容:无论出于何种原因,default
参数都被标记为可选。这意味着对foo.pop("x")
的调用实际上可以匹配两个重载。
但是,第一个重载变量的返回类型 是第二个重载变量的子类型:_VT
是Union[_VT, _T]
的子类型,无论最终_T
是什么存在。
如果我们将泛型添加到这些规则中,这些规则并不会真正改变。
文档中有关于the nuances of function overloading的更多详细信息/更多示例。