【问题标题】:why does mypy `cast` only work some of the time?为什么 mypy `cast` 只在某些时候起作用?
【发布时间】:2018-09-25 13:20:34
【问题描述】:

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

# 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 类,但它没有。

我的问题是为什么?

【问题讨论】:

  • 快速检查:如果您使用import lxml.html 而不仅仅是import lxml 会怎样?您当前的导入似乎不正确;您需要显式导入子模块。

标签: python mypy


【解决方案1】:

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

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

所以,它放弃了,只是假设它的类型为Any

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


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

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

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

【讨论】:

  • 感谢您的回答,但实际上并没有用。我彻底改变了我的问题以放大问题
  • @Sheena - 我的回答的要点仍然存在,即使在您编辑之后。没有可用的lxml 存根,因此mypy 假定您从中导入的任何内容都是Any 类型。特别是,变量lxml.html.HtmlElement 被假定为与“Any”完全等价的类型别名,而不是类——这就是强制转换为空操作的原因。相反,如果您在本地项目中添加一些文件,mypy 将对其进行分析 + 尽可能多地从中推断,无论它是否恰好包含类型注释。
  • 除非第三方库明确选择加入类型,否则不分析第三方库的决定是故意的——通常情况下,第三方库的编写方式非常动态/即将结束混淆了mypy。在这种特殊情况下,mypy 不会 能够分析 lxml,即使它想要 - lxml 是用 cython 编写并作为二进制文件分发的,而 mypy 没有能力理解任意二进制文件。
猜你喜欢
  • 2011-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-09
  • 1970-01-01
  • 2014-12-19
  • 1970-01-01
相关资源
最近更新 更多