python - 如何使格式不会导致类型提示错误?

标签 python python-3.x list-comprehension type-hinting mypy

我在 Python 中有以下列表推导式:

from typing import cast

# everything is fine
print([value for value in [1, 2, 3, 4]])

# on the first "value": Expression type contains "Any" (has type "List[Any]")
print("{}".format([value for value in [1, 2, 3, 4]]))

# on the "cast": Expression type contains "Any" (has type "List[Any]")
print("{}".format([cast(int, value) for value in [1, 2, 3, 4]]))

为什么使用 format 会导致 Mypy 返回错误?正如你所看到的,我尝试使用强制转换,但仍然失败。

This question看起来很相似,但我的特殊情况很奇怪,因为只要我不使用 format 函数,Mypy 似乎就很好(但使用 print 函数总是没问题) .

有什么办法可以避免格式化行给我带来错误吗? (或者我应该# type:ignore它们?)

编辑: 请注意,这似乎不仅仅是我的 Atom linter 的问题。 我使用的是 Mypy 版本 0.701,并在该文件上运行 Mypy,结果如下:

$ python3 -m mypy testing_list_iter.py --disallow-any-expr
testing_list_iter.py:7: error: Expression type contains "Any" (has type "List[Any]")
testing_list_iter.py:10: error: Expression type contains "Any" (has type "List[Any]")

最佳答案

这实际上与列表推导式无关:这实际上是 str.format(...) 的类型签名、mypy 如何执行类型推断以及 之间的不良交互>--disallow-any-expr 标志。

这是str.format(...)的类型签名,pulled from typeshed :

def format(self, *args: Any, **kwargs: Any) -> str: ...

当 mypy 对函数调用执行类型推断时,它将尝试使用声明的参数类型来帮助为您传入的表达式提供上下文。

因此,在这种情况下,由于这里的参数都是 Any,mypy 将意识到它可以简化通常需要执行的许多类型推断。因此,如果我们将任何列表文字传递到 str.format(...) 中,mypy 将决定“嘿,推断的类型可以只是 List[Any] ”。

下面是演示此行为的示例程序(使用 --disallow-any-expr 标志进行检查时):

from typing import cast, Any

def test1(x: Any) -> None:
    pass

def test2(x: object) -> None:
    pass

# Revealed type is 'builtins.list[Any]'
# Expression type contains "Any" (has type "List[Any]")
test1(reveal_type([1, 2, 3, 4]))

# Revealed type is 'builtins.list[builtins.int*]'
test2(reveal_type([1, 2, 3, 4]))

请注意,当我们尝试使用接受 object 而不是 Any 的函数时,mypy 将推断完整类型而不是执行此快捷方式。 (Mypy 从技术上讲可以做同样类型的快捷方式,因为所有类型也是 object 的子类,但我怀疑这只是更简单的实现方式 - 与 Any 不同,object 只是一个常规的普通旧类型,因此与它的特殊情况交互有点奇怪。)

通常,mypy 如何准确处理这种情况并不重要:无论哪种方式,您都可以获得准确的结果。

但是,--disallow-any-expr标志仍然是相当新的并且相对未经测试(对于很多人来说,它太激进了,尤其是那些试图在现有代码库上使用 mypy 的人) ,所以我们时不时会遇到这些糟糕的互动。


那么,解决办法是什么?

最好的解决办法是您向 Typeshed 提交一个拉取请求,修改 str.format(...)unicode.format(...)builtins.pyi文件,以便它们接受对象而不是 Any

此更改将符合 Typeshed 的 contribution guidelines无论如何 - 具体来说,“约定”部分中间的这段代码:

When adding type hints, avoid using the Any type when possible. Reserve the use of Any for when:

  • the correct type cannot be expressed in the current type system; and
  • to avoid Union returns (see above).

Note that Any is not the correct type to use if you want to indicate that some function can accept literally anything: in those cases use object instead.

然后,您等待 mypy 的下一个版本,理论上应该很快就会发布。

与此同时,您可以做的就是将列表理解的结果分配给一个新变量,然后将传递到str.format(...):

results = [value for value in [1, 2, 3, 4]]
print("{}".format(results))

这将导致 mypy 在没有 Any 上下文的情况下推断列表推导的类型,从而推断出完整的类型。这避免了与 --disallow-any-expr 标志的不良交互。

关于python - 如何使格式不会导致类型提示错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56625133/

相关文章:

python - 在python中打印排序和未排序的列表

javascript - 如何从 HTML 中抓取表格并在 Python 中创建 Excel 工作表?

python - 对系统资源使用上下文管理器有错吗?

Python - 用于列表理解的动态过滤器?

Python 列表理解- "pop"来自原始列表?

python - 使用两个可能的名称验证输入字段

python - 是否有文件对象的文档?

Python:Reduce() 和函数作为函数的参数

python - 列表索引超出范围,我不明白为什么?

python - 检查列表是否包含另一个列表中的项目