我怎样才能使格式不导致类型提示错误?
How can I get format to not cause a type-hinting error?
我在 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 返回错误?如您所见,我尝试使用转换,但仍然失败。
看起来很相似,但我的特殊情况很奇怪,因为只要我不使用 format
函数,Mypy 似乎就没问题(但 [=14 总是没问题的) =] 函数).
有什么办法可以避免格式错误的行出现? (或者我应该 # 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]")
这实际上与列表理解无关:这实际上是 [=13= 的类型签名、mypy 执行类型推断的方式和 --disallow-any-expr
标志之间的不良交互。
这是 str.format(...)
、pulled from typeshed 的类型签名:
def format(self, *args: Any, **kwargs: Any) -> str: ...
当 mypy 对函数调用执行类型推断时,它会尝试使用声明的参数类型来帮助为您传入的表达式提供上下文。
所以在这种情况下,因为这里的参数都是 Any
,mypy 会意识到它可以简化很多它通常需要做的类型推断。因此,如果我们将任何列表文字传入 str.format(...)
,mypy 将只决定 "hey, the inferred type can be just 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
的子类,但我怀疑它只是更简单 implementation-wise 而不是——不像 Any
、object
只是一个常规的 plain-old 类型,所以 special-casing 与它的交互有点奇怪。)
通常,mypy 如何准确处理这种情况并不重要:无论哪种方式,您都会得到准确的结果。
然而,--disallow-any-expr
标志仍然很新并且相对未经测试(它对很多人来说太激进了,尤其是那些试图在现有代码库上使用 mypy 的人),所以我们得到这些糟糕的交互不时。
那么,解决方法是什么?
最好的解决方法是向 Typeshed 提交拉取请求,修改 builtins.pyi 文件中的 str.format(...)
和 unicode.format(...)
,以便它们接受对象而不是 Any
.
无论如何,此更改将与 Typeshed 的 contribution guidelines 保持一致——具体来说,"Conventions" 部分中间的这个片段:
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的发布吧,理论上应该是soon-ish.
与此同时,您可以做的就是将列表理解的结果分配给一个新变量,然后将 that 传递给 str.format(...)
:
results = [value for value in [1, 2, 3, 4]]
print("{}".format(results))
这将导致 mypy 在没有 Any
上下文的情况下推断列表理解的类型,从而导致它推断出 full-fledged 类型。这避免了与 --disallow-any-expr
标志的不良交互。
我在 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 返回错误?如您所见,我尝试使用转换,但仍然失败。
format
函数,Mypy 似乎就没问题(但 [=14 总是没问题的) =] 函数).
有什么办法可以避免格式错误的行出现? (或者我应该 # 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]")
这实际上与列表理解无关:这实际上是 [=13= 的类型签名、mypy 执行类型推断的方式和 --disallow-any-expr
标志之间的不良交互。
这是 str.format(...)
、pulled from typeshed 的类型签名:
def format(self, *args: Any, **kwargs: Any) -> str: ...
当 mypy 对函数调用执行类型推断时,它会尝试使用声明的参数类型来帮助为您传入的表达式提供上下文。
所以在这种情况下,因为这里的参数都是 Any
,mypy 会意识到它可以简化很多它通常需要做的类型推断。因此,如果我们将任何列表文字传入 str.format(...)
,mypy 将只决定 "hey, the inferred type can be just 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
的子类,但我怀疑它只是更简单 implementation-wise 而不是——不像 Any
、object
只是一个常规的 plain-old 类型,所以 special-casing 与它的交互有点奇怪。)
通常,mypy 如何准确处理这种情况并不重要:无论哪种方式,您都会得到准确的结果。
然而,--disallow-any-expr
标志仍然很新并且相对未经测试(它对很多人来说太激进了,尤其是那些试图在现有代码库上使用 mypy 的人),所以我们得到这些糟糕的交互不时。
那么,解决方法是什么?
最好的解决方法是向 Typeshed 提交拉取请求,修改 builtins.pyi 文件中的 str.format(...)
和 unicode.format(...)
,以便它们接受对象而不是 Any
.
无论如何,此更改将与 Typeshed 的 contribution guidelines 保持一致——具体来说,"Conventions" 部分中间的这个片段:
When adding type hints, avoid using the
Any
type when possible. Reserve the use ofAny
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 useobject
instead.
那你就等下一个mypy的发布吧,理论上应该是soon-ish.
与此同时,您可以做的就是将列表理解的结果分配给一个新变量,然后将 that 传递给 str.format(...)
:
results = [value for value in [1, 2, 3, 4]]
print("{}".format(results))
这将导致 mypy 在没有 Any
上下文的情况下推断列表理解的类型,从而导致它推断出 full-fledged 类型。这避免了与 --disallow-any-expr
标志的不良交互。