MutableMapping pop()方法的类型提示定义

时间:2018-07-13 22:07:05

标签: python python-3.x type-hinting

我正在研究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参数仍未指定。类型检查器如何解决第三种类型...?

1 个答案:

答案 0 :(得分:1)

我本来打算在评论中留这个,但是我的回答开始变得有点长,所以这里是...

  

只能为MutableMapping指定两个参数:_KT和_VT。因此,此额外的_T参数仍未指定。类型检查器如何解决第三种类型...?

正如@jonrsharpe所说,_T 不是是类类型参数,它是函数类型参数。基本上,当您调用该方法时,mypy将具有:

  1. 已将_KT_VT绑定到其他某种类型。因此,如果我们执行foo: MutableMapping[str, int] = ...然后调用foo.pop(...),mypy将理解_KT_VT分别在strint处绑定。我们打电话给pop的时间。
  2. Mypy还会注意到,如果我们调用第二个重载,我们会有一个 free (又名 unbound )类型参数浮动。然后它将尝试推断根据我们传入的/周围环境的值,_T的正确类型是什么。

    例如,假设我做foo.pop("x", "bar")。这与第二个过载匹配。我们之前也曾说过_KT_VT分别绑定到strint。然后Mypy注意到_T是未绑定的,并尝试推断适当的类型。

    在这里,我们知道我们要传递的值的类型为“ str”,参数类型为Union[int, _T](在替换绑定类型之后)。我们也知道我们传入的值必须是参数的子类型-我们知道str必须是Union[int, _T]的子类型。

    然后Mypy使用上述所有信息/所有已知约束条件运行推理算法,并能够在这种情况下推断_T的类型必须为str

    < / li>

(作为一个附带说明,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使用了类似但更蓬松的即席算法)。

  1. 默认情况下,mypy将让任何两个重载的返回类型完全是任意的:它们彼此之间不需要任何固有的关系。
  2. 但是
  3. Mypy将禁止本来不安全的重载定义。在以下情况下,有两种过载变体固有地被认为是不安全的:

    1. 第一个变体的所有参数都与第二个变体兼容。
    2. 第一个变体的返回类型与第二个变体不兼容(例如,不是其子类型)。

    pop的特定情况下,第一个变量的参数确实与第二个变量兼容:无论出于何种原因,default参数都被标记为可选。这意味着对foo.pop("x")的调用实际上可以匹配两个重载。

    但是,第一个重载变量的返回类型 是第二个重载变量的子类型:_VTUnion[_VT, _T]的子类型,无论最终_T是什么存在。

  4. 当您实际调用重载时,mypy会按照定义的顺序检查可用的重载,并使用匹配的第一个变体。为了确保该算法的行为符合预期,如果以某种方式无法定义某些重载的方式定义重载,mypy将报告错误。

如果我们将泛型添加到这些规则中,这些规则并不会真正改变。

文档中有关于the nuances of function overloading的更多详细信息/更多示例。