子类中返回值的类型提示 [英] type hint for return value in subclass

查看:34
本文介绍了子类中返回值的类型提示的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个 CustomEnum 类,我想在其中添加一些帮助器方法,然后子类化我的 CustomEnum 的类可以使用这些方法.其中一种方法是返回一个随机枚举值,这就是我被卡住的地方.该函数按预期工作,但在类型提示方面,我想不出一种说法返回类型与 cls 类型相同".

I am writing a CustomEnum class in which I want to add some helper methods, that would then be available by the classes subclassing my CustomEnum. One of the methods is to return a random enum value, and this is where I am stuck. The function works as expected, but on the type-hinting side, I cannot figure out a way of saying "the return type is the same type of cls".

我相当确定其中涉及到一些 TypeVar 或类似的魔法,但由于我从未使用过它们,因此我从未花时间弄清楚它们.

I am fairly sure there's some TypeVar or similar magic involved, but since I never had to use them I never took the time to figure them out.

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")

有人可以帮助我或给我一个关于如何进行的提示吗?

Can somebody help me or give me a hint on how to proceed?

谢谢!

推荐答案

这里的语法有点可怕,但我认为没有更简洁的方法来做到这一点.

The syntax here is kind of horrible, but I don't think there's a cleaner way to do this.

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.)

(In python versions <= 3.8, you have to use typing.Type rather than the builtin type if you want to parameterise it.)

这是怎么回事?

T 在顶部定义为绑定"类型变量.到 CustomEnum 类.这意味着用 T 注释的变量只能是 CustomEnum 的实例或继承自 CustomEnum 的类的实例.

T is defined at the top as being a type variable that is "bound" to the CustomEnum class. This means that a variable annotated with T can only be an instance of CustomEnum or an instance of a class inheriting from CustomEnum.

在上面的类方法中,我们实际上是使用这个类型变量来定义与返回类型相关的 cls 参数的类型.通常我们做相反的事情——我们通常根据函数的输入参数的类型定义函数的返回类型.因此,如果这感觉有点令人费解,这是可以理解的!

In the classmethod above, we're actually using this type-variable to define the type of the cls parameter with respect to the return type. Usually we do the opposite — we usually define a function's return types with respect to the types of that function's input parameters. So it's understandable if this feels a little mind-bending!

我们说:这个方法会导致一个类的实例——我们不知道这个类是什么,但我们知道它要么是CustomEnum,要么是一个继承自自定义枚举.我们也知道无论返回什么类,我们都可以保证函数中cls参数的类型是上一级"的.在来自返回值类型的类型层次结构中.

We're saying: this method leads to instances of a class — we don't know what the class will be, but we know it will either be CustomEnum or a class inheriting from CustomEnum. We also know that whatever class is returned, we can guarantee that the type of the cls parameter in the function will be "one level up" in the type heirarchy from the type of the return value.

在很多情况下,我们可能知道 type[cls] 将始终是一个固定值.在这些情况下,可以将其硬编码到类型注释中.但是,最好不要这样做,而是使用这种方法,它清楚地显示了输入类型和返回类型之间的关系(即使它使用了可怕的语法来这样做!).

In a lot of situations, we might know that type[cls] will always be a fixed value. In those situations, it would be possible to hardcode that into the type annotations. However, it's best not to do so, and instead to use this method, which clearly shows the relationship between the type of the input and the return type (even if it uses horrible syntax to do so!).

进一步的解释和例子

对于绝大多数类(不使用 Enums,它们使用元类,但让我们暂时将其搁置一旁),以下将适用:

For the vast majority of classes (not with Enums, they use metaclasses, but let's leave that aside for the moment), the following will hold true:

示例 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

对于 CustomEnum.random() 方法的 cls 参数,我们注释了 A 而不是 instance_of_a 在我上面的示例 1 中.

For the cls parameter of your CustomEnum.random() method, we're annotating the equivalent of A rather than instance_of_a in my example 1 above.

  • instance_of_a 的类型是 A.
  • 但是A的类型不是A——A是一个类,而不是一个类的实例.
  • 类不是类的实例;它们要么是 type 的实例,要么是从 type 继承的自定义元类的实例.
  • 这里没有使用元类;所以,A的类型是type.
  • The type of instance_of_a is A.
  • But the type of A is not AA is a class, not an instance of a class.
  • Classes are not instances of classes; they are either instances of type or instances of custom metaclasses that inherit from type.
  • No metaclasses are being used here; ergo, the type of A is type.

规则如下:

  • 所有 python 类实例的类型将是它们所属的类.
  • 所有 python 的类型要么是 type,要么是(如果你太聪明了)从 继承的自定义元类输入.
  • The type of all python class instances will be the class they're an instance of.
  • The type of all python classes will be either type or (if you're being too clever for your own good) a custom metaclass that inherits from type.

使用您的 CustomEnum 类,我们可以enum 模块使用的元类注释 cls 参数(enum.EnumType, 如果你想知道).但是,正如我所说 - 最好不要.我建议的解决方案更清楚地说明了输入类型和返回类型之间的关系.

With your CustomEnum class, we could annotate the cls parameter with the metaclass that the enum module uses (enum.EnumType, if you wanna know). But, as I say — best not to. The solution I've suggested illustrates the relationship between the input type and the return type more clearly.

这篇关于子类中返回值的类型提示的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆