Python mypy 检查 TypeVar(bound=Union[A, B]) 的返回类型不会出错,而 TypeVar(A, B) 会出错

Int*_*rer 6 python generics static-analysis mypy

我一直试图理解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
Run Code Online (Sandbox Code Playgroud)

运行时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]) 时,以下内容是合法的:

  1. Union[Enum1, Enum2]
  2. Enum1
  3. Enum2

当使用情况 2 ( A, B) 时,以下是合法的:

  1. Enum1
  2. Enum2

但是,我认为这个答案并不能解释我的问题,我没有使用这个Union案例。

谁能告诉我发生了什么事吗?

Ken*_*ung 3

我认为发生错误是因为类型检查器没有足够的信息来通过查看输入参数的类型来推断返回类型。虽然操控性可能会得到改善。

假设您有一个简单的通用函数:

Enums = TypeVar("Enums", Enum1, Enum2)

def add(x: Enums, y: Enums) -> Enums:
    return x
Run Code Online (Sandbox Code Playgroud)

类型检查器可以通过输入参数的类型推断返回类型:

add(Enum2.MEMBER3, Enum2.MEMBER4) # ok, return Enum2
add(Enum1.MEMBER1, Enum1.MEMBER2) # ok, return Enum1

add(Enum2.MEMBER3, Enum1.MEMBER2) # not ok
Run Code Online (Sandbox Code Playgroud)

再次查看您的函数_enum_to_num,类型检查器无法推断返回类型,它只是不知道将返回什么类型,因为它不知道将返回什么类型cast_enum

def _enum_to_num(val: int, cast_enum: EnumMeta) -> Enums:
    return cast_enum(val)
Run Code Online (Sandbox Code Playgroud)

静态类型检查的想法是,它在不执行的情况下评估代码,它研究变量的类型,而不是动态。通过查看 的类型cast_enum,即EnumMeta,类型检查器无法判断是否cast_enum会返回Enums。看起来它只是假设它会返回Enum1,并且它导致了错误_enum_to_num(val, Enum2)

你知道它_enum_to_num(val, Enum2)会返回,Enum2因为你知道is的。该值是类型检查器通常不会触及的东西。这可能会令人困惑,变量的是,而类型是,尽管是类型。cast_enumEnum2cast_enumEnum2cast_enumEnumMetaEnum2

这个问题可以通过告诉类型检查器类型将通过cast_enum使用来解决typing.Type

from typing import TypeVar, Union, Type

...

def _enum_to_num(val: int, cast_enum: Type[Enums]) -> Enums:
    return cast_enum(val)
Run Code Online (Sandbox Code Playgroud)

该错误将消失,因为现在类型检查器可以推断返回类型。