如何获得格式而不导致类型提示错误?

Pro*_*o Q 2 python list-comprehension type-hinting python-3.x 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]]))
Run Code Online (Sandbox Code Playgroud)

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

这个问题看起来很相似,但我的特殊情况很奇怪,因为只要我不使用该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]")
Run Code Online (Sandbox Code Playgroud)

Mic*_*x2a 5

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

下面是类型签名str.format(...)从typeshed拉

def format(self, *args: Any, **kwargs: Any) -> str: ...
Run Code Online (Sandbox Code Playgroud)

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

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

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

def format(self, *args: Any, **kwargs: Any) -> str: ...
Run Code Online (Sandbox Code Playgroud)

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

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

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


那么,有什么解决办法呢?

最好的可能的修复将是你出力拉请求Typeshed修改str.format(...),并unicode.format(...)builtins.pyi所以他们接受的对象,而不是文件Any

无论如何,这种变化都符合 Typeshed 的贡献指南——特别是,“约定”部分中间的这个片段:

添加类型提示Any时,尽可能避免使用类型。保留在以下情况下使用Any

  • 在当前的类型系统中无法表达正确的类型;和
  • 避免联盟返回(见上文)。

请注意,Any如果您想表明某些函数可以接受任何字面意思,则这不是正确的类型:在这些情况下,请object改用。

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

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

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]))
Run Code Online (Sandbox Code Playgroud)

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