在以两种不同的方式使用TypeVar
时,我一直试图了解其边界:
Enums = TypeVar("Enums", Enum1, Enum2)
Enums = TypeVar("Enums", bound=Union[Enum1, Enum2])
这是我正在使用的代码:
#!/usr/bin/env python3.6
"""Figuring out why enum is saying incompatible return type."""
from enum import IntEnum, EnumMeta
from typing import TypeVar, Union
class Enum1(IntEnum):
MEMBER1 = 1
MEMBER2 = 2
class Enum2(IntEnum):
MEMBER3 = 3
MEMBER4 = 4
# Enums = TypeVar("Enums", bound=Union[Enum1, Enum2]) # Case 1... Success
Enums = TypeVar("Enums", Enum1, Enum2) # Case 2... error: Incompatible return value
def _enum_to_num(val: int, cast_enum: EnumMeta) -> Enums:
return cast_enum(val)
def get_some_enum(val: int) -> Enum1:
return _enum_to_num(val, Enum1)
def get_another_enum(val: int) -> Enum2:
return _enum_to_num(val, Enum2) # line 35
运行mypy==0.770
时:
Case 1
:Success: no issues found
Case 2
:35: error: Incompatible return value type (got "Enum1", expected "Enum2")
这种情况非常类似于该问题:Difference between TypeVar('T', A, B) and TypeVar('T', bound=Union[A, B])
答案说明在使用情况1(bound=Union[Enum1, Enum2]
)时,以下内容是合法的:
Union[Enum1, Enum2]
Enum1
Enum2
在使用案例2(A, B
)时,以下内容是合法的:
Enum1
Enum2
但是,我认为此答案不能解释我的问题,我没有使用Union
情况。
任何人都可以告诉我发生了什么事吗?
答案 0 :(得分:2)
我认为发生错误是因为类型检查器没有足够的信息通过查看输入参数的类型来推断。尽管处理可能会有所改善。
假设您有一个简单的通用函数:
Enums = TypeVar("Enums", Enum1, Enum2)
def add(x: Enums, y: Enums) -> Enums:
return x
类型检查器可以通过输入参数的类型来推断返回类型:
add(Enum2.MEMBER3, Enum2.MEMBER4) # ok, return Enum2
add(Enum1.MEMBER1, Enum1.MEMBER2) # ok, return Enum1
add(Enum2.MEMBER3, Enum1.MEMBER2) # not ok
再次查看函数_enum_to_num
,类型检查器无法推断返回类型,它只是不知道将返回什么类型,因为它不知道{ {1}}:
cast_enum
静态类型检查的思想是,它无需执行即可评估代码,它研究变量的 types ,而不是动态的 values 。通过查看def _enum_to_num(val: int, cast_enum: EnumMeta) -> Enums:
return cast_enum(val)
的类型,即cast_enum
,类型检查器无法判断EnumMeta
是否将返回cast_enum
。看起来它只是假设它将返回Enums
,并在Enum1
中导致错误。
您知道_enum_to_num(val, Enum2)
将返回_enum_to_num(val, Enum2)
,因为您知道Enum2
的值为cast_enum
。 value 是 type 检查器通常不会碰到的东西。可能令人困惑,变量Enum2
的 value 为cast_enum
,而Enum2
的 type 为{{1} },尽管cast_enum
是类型。
可以通过告诉类型检查器使用typing.Type
通过EnumMeta
传递类型来解决此问题:
Enum2
该错误将消失,因为现在类型检查器可以推断出返回类型。
答案 1 :(得分:1)
我先写一些关于mypy的见解和报告,然后再问这是否是mypy的错误。
消息:
Incompatible return value type (got "Enum1", expected "Enum2")
在这里意味着大致Enum2
或它的子类型。 Enum2
是get_another_enum()
的声明返回值。但是mypy认为函数调用_enum_to_num()
返回的是Enum1
类型。
“大致”部分是因为当类型是未绑定的,或者是Any
或Union
类型时,存在类型检查的例外;但这不适用于本示例。
Mypy认为cast_enum()
中的函数_enum_to_num()
返回Enums
中列出的 first 类型-我想作为静态类型检查器,它必须选择一个,这就是它的作用。
因此,如果您在Enums
分配中切换顺序并输入:
Enums = TypeVar("Enums", Enum2, Enum1) # Case 2... error: Incompatible return value
然后第35行将成功,但是get_some_enum()
中的返回将失败,并显示以下消息:
error: Incompatible return value type (got "Enum2", expected "Enum1")
关于这是否是mypy bug,很难告诉...
使用type()
或ininstance()
函数可以在此处找到动态类型的错误; 运行该代码也可以按预期工作。
另一方面,Python从不检查返回类型,无论是在编译时还是在运行时:您可以将_enum_to_none()
的返回类型更改为None
,并且到目前为止仍然有效就Python解释器而言。
然后问题归结为:在mypy施加的静态类型系统中,这是一个错误吗? (我不认为PEP 484、526或其他数字会尝试解决这一问题)。
更有资格的人应该回答这个问题,是否应该由静态分析器(尤其是mypy)捕获。
有关更明确地消除mypy的错误的方法,请参见Ken Hung的答案。