为什么 mypy 在枚举类方法中失败并显示 "incompatible type"?

Why does mypy fail with "incompatible type" in Enum classmethod?

在我的 Enum 中,我定义了一个 class 方法来将给定值强制转换为 Enum 成员。给定的值可能已经是 Enum 的一个实例,或者它可能是一个包含 Enum 值的字符串。 为了确定它是否需要转换,我检查参数是否是 class 的实例,如果不是,则只将其传递给 int()。此时——根据参数 'item' 的类型提示——它必须是一个字符串。

class 看起来像这样:

T = TypeVar('T', bound='MyEnum')

class MyEnum(Enum):
    A = 0
    B = 1

    @classmethod
    def coerce(cls: Type[T], item: Union[int, T]) -> T:
        return item if isinstance(item, cls) else cls(int(item))

mypy 失败:

error: Argument 1 to "int" has incompatible type "Union[str, T]"; expected "Union[str, bytes, SupportsInt, SupportsIndex, _SupportsTrunc]"

为什么?

因为int不能从Enum构造出来。来自 documentationint(x)

If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in radix base

所以为了让它工作,你可以让你的枚举继承自 str

class MyEnum(str, Enum):
    A = 0
    B = 1

或使用IntEnum

class MyEnum(IntEnum):
    A = 0
    B = 1

编辑:为什么 isinstance 没有缩小 T 类型的问题仍然存在。找不到我的理论的任何证据,但我的(希望是合乎逻辑的)解释是 - T 受限于 MyEnum 所以它是 MyEnum 和它的任何子 class。如果我们有 subclass 场景,isinstance(item, MyEnumSubclass) 不会排除项目只是 MyEnum class 的情况。如果我们直接在输入中使用MyEnum,问题就会消失。

    @classmethod
    def coerce(cls: t.Type["MyEnum"], item: t.Union[int, T]) -> "MyEnum":
        return item if isinstance(item, cls) else cls(int(item))