子类中返回值的类型提示
我正在编写一个 CustomEnum 类,我想在其中添加一些辅助方法,然后子类化我的 CustomEnum 的类可以使用这些方法。其中一种方法是返回一个随机枚举值,这就是我被卡住的地方。该函数按预期工作,但在类型提示方面,我无法想出一种说法“返回类型与 cls 类型相同”。
我相当确定其中TypeVar涉及一些或类似的魔法,但由于我从未使用过它们,因此我从未花时间弄清楚它们。
class CustomEnum(Enum):
@classmethod
def random(cls) -> ???:
return random.choice(list(cls))
class SubclassingEnum(CustomEnum):
A = "a"
B = "b"
random_subclassing_enum: SubclassingEnum
random_subclassing_enum = SubclassingEnum.random() # Incompatible types in assignment (expression has type "CustomEnum", variable has type "SubclassingEnum")
有人可以帮助我或给我一个关于如何进行的提示吗?
谢谢!
回答
这里的语法有点可怕,但我认为没有更简洁的方法来做到这一点。
from typing import TypeVar
from enum import Enum
T = TypeVar("T", bound="CustomEnum")
class CustomEnum(Enum):
@classmethod
def random(cls: type[T]) -> T:
return random.choice(list(cls))
(在 python 版本 <= 3.8 中,如果要对其进行参数化,则必须使用typing.Type而不是内置type函数。)
这里发生了什么?
T在顶部定义为“绑定”到CustomEnum类的类型变量。这意味着用 注释的变量T只能是CustomEnum从 继承的类的实例或实例CustomEnum。
在上面的类方法中,我们实际上是使用这个类型变量来定义cls与返回类型相关的参数类型。通常我们做相反的事情——我们通常根据函数的输入参数的类型定义函数的返回类型。因此,如果这感觉有点令人费解,这是可以理解的!
我们是说:这个方法会导致一个类的实例——我们不知道这个类会是什么,但我们知道它会是或者是CustomEnum一个继承自 的类CustomEnum。我们也知道,无论返回什么类,我们都可以保证cls函数中参数的类型在类型层次结构中比返回值的类型“上一层”。
在很多情况下,我们可能知道这type[cls]将始终是一个固定值。在这些情况下,可以将其硬编码到类型注释中。但是,最好不要这样做,而是使用这种方法,它清楚地显示了输入类型和返回类型之间的关系(即使它使用了可怕的语法来这样做!)。
进一步的解释和例子
对于绝大多数类(不使用Enums,它们使用元类,但让我们暂时将其搁置一旁),以下内容将适用:
示例 1
Class A:
pass
instance_of_a = A()
type(instance_of_a) == A # True
type(A) == type # True
例子2
class B:
pass
instance_of_b = B()
type(instance_of_b) == B # True
type(B) == type # True
对于cls你的参数CustomEnum.random()的方法,我们标注的等效A,而不是instance_of_a在我的例子1以上。
- 的类型
instance_of_a是A。 - 但类型
A不是A——A是一个类,而不是一个类的实例。 - 类不是类的实例;它们是
type继承自 的自定义元类的实例或实例type。 - 这里没有使用元类;因此, 的类型
A是type。
规则如下:
- 所有 python类实例的类型将是它们所属的类。
- 所有 python类的类型将是
type或者(如果你对自己的好处太聪明了)继承自type.
有了您的CustomEnum类,我们可以注释cls与元类,参数enum模块使用(enum.EnumType,如果你想知道)。但是,正如我所说 - 最好不要。我建议的解决方案更清楚地说明了输入类型和返回类型之间的关系。