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

问题描述 投票:1回答:1

我被卡住了,试图理解 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")

:: 此案是 与本题类似。TypeVar('T', A, B)和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 的情况。

谁能告诉我这是怎么回事?

python generics static-analysis mypy
1个回答
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 再次,类型检查器没有办法推断返回类型,它只是不知道会返回什么类型,因为它不知道会被 cast_enum:

def _enum_to_num(val: int, cast_enum: EnumMeta) -> Enums:
    return cast_enum(val)

静态类型检查的想法是,它在不执行的情况下评估代码,它调查的是 类型 变量的,而不是动态的 价值观. 通过观察以下类型 cast_enumEnumMeta,类型检查器无法判断是否 cast_enum 将返回 Enums 或不。看来它只是假设会返回 Enum1的错误,并导致 _enum_to_num(val, Enum2).

你知道的 _enum_to_num(val, Enum2) 将返回 Enum2 因为你知道 价值cast_enumEnum2. 该 价值 是什么 类型 检查器一般不碰。这可能会让人感到困惑。价值 变化的 cast_enumEnum2类型cast_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)

这个错误会消失,因为现在类型检查器可以推断出返回类型。


1
投票

我先写一点关于mypy看到的和报告的内容,然后再问这是否是mypy的bug。

这个消息。

Incompatible return value type (got "Enum1", expected "Enum2")

在这里的意思是,大概是指一个 Enum2 的子类型。Enum2 的声明返回值。get_another_enum(). 然而,mypy认为函数调用 _enum_to_num() 正在返回一个 Enum1 类型。

大概 "的部分是因为当一个类型是未绑定的,或者是一个 AnyUnion 类型;但在本例中并不适用。

Mypy决定将函数 cast_enum()_enum_to_num() 正在返回 第一Enums - 我想作为一个静态类型检查器,它必须选择一个,这就是它的工作。

所以如果你把 Enums 赋值并写入。

Enums = TypeVar("Enums", Enum2, Enum1)  # Case 2... error: Incompatible return value

那么第35行就会成功,但在第35行中的回车就会成功。get_some_enum() 会出现失败的信息。

error: Incompatible return value type (got "Enum2", expected "Enum1")

至于这是不是mypy的错误,很难说... ...

这里没有动态类型的错误,你可以通过使用 type()ininstance() 职能。运行 的代码也能正常工作。

另一方面,Python 从不检查返回类型,不管是在编译时还是在运行时:你可以改变一下 _enum_to_none() 将要 None 而就 Python 解释器而言,这仍然是有效的。

那么问题就来了:在mypy强加的静态类型系统中,这是不是一个bug?(我不认为PEP 484、526或其他数字试图解决这个问题)。

应该有更有资格的人回答这个问题,这是否是一个应该被静态分析器,尤其是mypy抓住的bug。

请看Ken Hung的回答,他的回答可以更明确,并删除mypy的错误。

© www.soinside.com 2019 - 2024. All rights reserved.