首页 > 解决方案 > 为什么 mypy `cast` 只在某些时候起作用?

问题描述

首先我做了一个愚蠢的小班

# cheese_helpers.py
class Cheese:
    pass

然后这件事发生了

# weird.py

import lxml
from typing import cast, List
import cheese_helpers

o: List[Any] = []
reveal_type(o) # builtins.List[Any] as expected

y = cast(List[cheese_helpers.Cheese], o)  
reveal_type(y)  # builtins.List[cheese_helpers.Cheese], as expected

# so far so good. And then:

z = cast(List[lxml.html.HtmlElement], o)  
reveal_type(z) # builtins.List[Any] ???????????????

List[lxml.html.HtmlElement]如果你问我,最后一行应该是。奶酪也没有注释,效果很好。

我确定要使最后一行正常工作,我需要获取/制作一些 lxml 注释。cast但对我来说,我的完全被忽略似乎很奇怪。我投到奶酪类,它的工作原理。我投射到 HtmlElement 类,但它没有。

我的问题是为什么?

标签: pythonmypy

解决方案


您是否正在使用旧版本的 mypy?当我尝试使用 mypy 0.630(pypi 上的最新版本)和他们的 git master 分支上的最新代码对您的代码进行类型检查时,我builtins.list[Any]在所有三种情况下都得到了一个显示的类型。

这种显示的类型希望更直观一点——问题是很遗憾 typeshed 上的库没有可用的存根lxml这意味着 mypy 没有关于真正内容的信息。(所有 mypy 都知道,这可能是一个类、一个函数、一个变量、一个类型别名、一个命名元组......)html.HtmlElement

所以,它放弃了,只是假设它有一个Any.

这也解释了为什么将输出分配get_some_relevant_elements给一个List[bool]没有错误的作品。一个类型的变量List[Any]理论上可以包含任何东西,包括布尔值——所以也许这是一个安全的赋值。


无论如何,如果您不喜欢这种行为,您的两个选择是:

  1. 接受lxml库没有类型提示/纯粹是动态类型的,并设计您的代码,以使所有动态都包含在一个位置。当您从 XML 文件中提取信息时,(可选地)验证它们并返回您自己的自定义、带注释的类。基本上,故意在代码库的动态和非动态部分之间设置障碍。

  2. 为 lxml 创建自己的存根。这些存根不一定需要很复杂——只需为您需要的少数类和方法创建初步存根就足够了。(如果它们最终变得相当充实,如果你愿意的话,你也许可以开源它们并回馈社区。)


推荐阅读