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

时间:2019-06-17 05:22:53

标签: python python-3.x list-comprehension typing 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看起来很相似,但是我的特殊情况很奇怪,因为Mypy看起来还不错,只要我不使用format函数即可(但是print总是可以的功能)。

有什么我可以做的,不用格式化的行给我错误吗? (还是我应该# type: ignore?)

编辑: 请注意,这似乎不仅仅是我的Atom短绒棉的问题。 我使用的是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]")

1 个答案:

答案 0 :(得分:4)

这实际上与列表理解无关:实际上,这是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贡献拉动请求,以修改builtins.pyi文件中的str.format(...)unicode.format(...),以便它们接受对象而不是Any

无论如何,此更改将与Typeshed的contribution guidelines一致-具体来说,此代码段位于“公约”部分的中间:

  

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

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

请注意,Any不是正确的类型,如果您想表明某个函数可以接受字面上的任何内容:在这种情况下,请使用object

然后,您等待mypy的下一个发行版,从理论上讲,它应该是很快的版本。

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

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

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