【问题标题】:Why does mypy fail with "incompatible type" in Enum classmethod?为什么 mypy 在 Enum 类方法中因“不兼容的类型”而失败?
【发布时间】:2022-01-07 17:23:12
【问题描述】:

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

类如下所示:

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 失败:

错误:“int”的参数 1 具有不兼容的类型“Union[str, T]”; 预期“联合[str,字节,SupportsInt,SupportsIndex, _SupportsTrunc]"

为什么?

【问题讨论】:

    标签: python mypy


    【解决方案1】:

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

    如果 x 不是数字或者如果给出了基数,那么 x 必须是 字符串bytes,或 bytearray 实例,表示基数中的整数文字 基地

    所以要让它工作,你可以让你的 Enum 从 str 继承

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

    或使用IntEnum

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

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

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

    【讨论】:

    • 不会将if isinstance(item, cls) 用作类型保护以避免从MyEnum 的实例构造int
    • @kosciej16 不,如果您使用 intsavailable 的字符串化版本调用,它实际上确实有效。 MyEnum.coerce('0') ->
    • 然而,从 IntEnum 派生,确实让 mypy 平静下来,似乎是一个优雅的解决方案。仍然不完全明白为什么“守卫和施放”的方法如此令人不安。
    • 在使用 mypy 的东西和reveal_type 进行一些试验后,我想我找到了解释。
    • 另一个与推理相同的事情是def coerce(cls: Type['MyEnum'], item: Union[str, 'MyEnum']) -> 'MyEnum':的方法签名和return item if isinstance(item, MyEnum) else cls(int(item))的返回值
    猜你喜欢
    • 2022-06-14
    • 2019-06-18
    • 2021-07-10
    • 1970-01-01
    • 2020-08-01
    • 2017-10-10
    • 2019-08-23
    • 1970-01-01
    • 2021-11-22
    相关资源
    最近更新 更多