在子类中键入 return 值的提示
Type hint for return value in subclass
我正在编写一个 CustomEnum class,我想在其中添加一些辅助方法,然后 classes subclassing 我的 CustomEnum 可以使用这些方法。其中一种方法是 return 一个随机枚举值,这就是我被卡住的地方。该函数按预期工作,但在类型提示方面,我无法想出一种说法 "the return type is the same type of 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")
有人可以帮助我或提示我如何进行吗?
谢谢!
这里的语法有点糟糕,但我认为没有更简洁的方法来执行此操作。以下passes MyPy:
from typing import TypeVar
from enum import Enum
import random
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
class 的“bound”。这意味着用 T
注释的变量只能是 CustomEnum
的实例或继承自 CustomEnum
.
的 class 的实例
在上面的 class 方法中,我们实际上是使用这个类型变量来定义 cls
参数相对于 return 类型的类型。通常我们做相反的事情——我们通常根据函数输入参数的类型定义函数的 return 类型。因此,如果这感觉有点令人费解,这是可以理解的!
我们说:这个方法会导致 class 的实例——我们不知道 class 会是什么,但我们知道它要么是 CustomEnum
或者 class 继承自 CustomEnum
。我们还知道,无论 class 是 returned,我们可以保证函数中 cls
参数的类型在类型层次结构中从return 值。
在很多情况下,我们可能知道type[cls]
永远是一个固定值。在那些情况下,可以将其硬编码到类型注释中。但是,最好 [=77=] 不要 这样做,而是使用这种方法,它清楚地显示了输入类型与 return 类型之间的关系(即使它使用这样做的语法太糟糕了!)。
Further reading: the MyPy documentation on the type of class objects.
进一步的解释和示例
对于绝大多数classes(不包括Enum
es,他们使用metaclasses,但让我们暂时搁置),以下将成立真:
示例 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
参数,我们在我的 示例 1 中注释了等同于 A
而不是 instance_of_a
以上。
instance_of_a
的类型是A
。
- 但是
A
的类型不是 A
— A
是 class,不是 class.[=96= 的实例]
- 类 不是 classes 的实例;它们要么是
type
的实例,要么是继承自 type
. 的自定义元 classes 的实例
- 这里没有使用元class;因此,
A
的类型是 type
.
规则如下:
- 所有 python class 实例的类型 将是 class 它们的实例。
- 所有 python classes 的类型将是
type
或(如果你对自己太聪明的话好)一个自定义元 class 继承自 type
.
使用您的 CustomEnum
class,我们 可以 使用元 class 注释 cls
参数 enum
模块使用 (enum.EnumType
, if you want to know)。但是,正如我所说 - 最好不要。我建议的解决方案更清楚地说明了输入类型和 return 类型之间的关系。
我正在编写一个 CustomEnum class,我想在其中添加一些辅助方法,然后 classes subclassing 我的 CustomEnum 可以使用这些方法。其中一种方法是 return 一个随机枚举值,这就是我被卡住的地方。该函数按预期工作,但在类型提示方面,我无法想出一种说法 "the return type is the same type of 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")
有人可以帮助我或提示我如何进行吗?
谢谢!
这里的语法有点糟糕,但我认为没有更简洁的方法来执行此操作。以下passes MyPy:
from typing import TypeVar
from enum import Enum
import random
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
class 的“bound”。这意味着用 T
注释的变量只能是 CustomEnum
的实例或继承自 CustomEnum
.
在上面的 class 方法中,我们实际上是使用这个类型变量来定义 cls
参数相对于 return 类型的类型。通常我们做相反的事情——我们通常根据函数输入参数的类型定义函数的 return 类型。因此,如果这感觉有点令人费解,这是可以理解的!
我们说:这个方法会导致 class 的实例——我们不知道 class 会是什么,但我们知道它要么是 CustomEnum
或者 class 继承自 CustomEnum
。我们还知道,无论 class 是 returned,我们可以保证函数中 cls
参数的类型在类型层次结构中从return 值。
在很多情况下,我们可能知道type[cls]
永远是一个固定值。在那些情况下,可以将其硬编码到类型注释中。但是,最好 [=77=] 不要 这样做,而是使用这种方法,它清楚地显示了输入类型与 return 类型之间的关系(即使它使用这样做的语法太糟糕了!)。
Further reading: the MyPy documentation on the type of class objects.
进一步的解释和示例
对于绝大多数classes(不包括Enum
es,他们使用metaclasses,但让我们暂时搁置),以下将成立真:
示例 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
参数,我们在我的 示例 1 中注释了等同于 A
而不是 instance_of_a
以上。
instance_of_a
的类型是A
。- 但是
A
的类型不是A
—A
是 class,不是 class.[=96= 的实例] - 类 不是 classes 的实例;它们要么是
type
的实例,要么是继承自type
. 的自定义元 classes 的实例
- 这里没有使用元class;因此,
A
的类型是type
.
规则如下:
- 所有 python class 实例的类型 将是 class 它们的实例。
- 所有 python classes 的类型将是
type
或(如果你对自己太聪明的话好)一个自定义元 class 继承自type
.
使用您的 CustomEnum
class,我们 可以 使用元 class 注释 cls
参数 enum
模块使用 (enum.EnumType
, if you want to know)。但是,正如我所说 - 最好不要。我建议的解决方案更清楚地说明了输入类型和 return 类型之间的关系。