为了说明这个问题,我定义了一个PositiveNumber
类,它是一个Number
类的子类。由于Python不支持类型转换,所以我将from_number
定义为一种方便的方法,可以用来将一个实例强制转换到另一个实例。这个方法对于Number
类的所有子类型都是通用的,所以我将它放入父类中。
from __future__ import annotations
from typing import Any, Dict
class Number:
def __init__(self, value: float, *args, **kwargs) -> None:
self.value = value
@classmethod
def from_number(cls, other: Number) -> Number:
return cls(**other.dict())
def dict(self) -> Dict[str, Any]:
return {
"value": self.value
}
class PositiveNumber(Number):
def __init__(self, value: float, *args, **kwargs) -> None:
if value <= 0:
raise ValueError("Value must be a positive float value.")
super().__init__(value)
@property
def is_positive(self) -> bool:
return True
x = Number(1001) # this could be a positive number
# instead of writing this
y = PositiveNumber(value=x.value)
# I would like to be able to do this
y = PositiveNumber.from_number(x)
type(y)
# >> <class '__main__.PositiveNumber'>
正如我们所看到的,y是PositiveNumber
的一个实例,正如我们所期望的,但它被注释为Number
的一个实例。
为了避免这种情况,我必须在每个子类中覆盖from_number
,以更正输出注释。
class PositiveNumber(Number):
def __init__(self, value: float, *args, **kwargs) -> None:
if value <= 0:
raise ValueError("Value must be a positive float value.")
super().__init__(value)
@classmethod
def from_number(cls, other: Number) -> PositiveNumber: #overridden
return super().from_number(other)
@property
def is_positive(self) -> bool:
return True
我的想法是,from_number
应该是一个接口方法,但是接下来我必须在每个子类型中实现相同的功能,这将是很乏味的,因为代码大部分是相同的。
是否有一种更优雅的方式来做到这一点,或者这是一个完全糟糕的设计?
发布于 2022-01-07 01:27:19
在类型理论中,你需要的概念称为MyType
或SelfType
。正如您所发现的,这是一个非常有用的特性,有两个主要的用例(clone
/ copy
/ dup
方法和工厂/解析/反序列化方法),而且我总是感到惊讶的是,几乎没有主流类型系统具有这个特性。我脑子里唯一的例外是TypeScript和生锈。
[注意: Scala也有一个名为自型的概念,但这是另一回事:它允许您将类型分配给this
,而不是引用this
类型。它还提供了单例类型,允许您引用仅由特定对象居住的类型,包括类似于this.type
的内容。但是这太严格了:这指的是一个类型,其唯一的实例是当前的this
,而不是与this
具有相同类型的任何实例。]
对于Python和MyPy,您既幸运又不走运:好消息是Python有一个PEP 673中的自定义规范草案:
from typing import Any, Dict, Self
class Number:
@classmethod
def from_number(cls, other: Number) -> Self:
return cls(**other.dict())
坏消息是,它仍然是一个草案,计划在Python3.11中实现,目前它是计划于2022年-第四季度发布。它已经在皮赖特和MyPy的叉子中实现了,但是它还没有在typing
中实现(但是有一个拉请求),也没有在MyPy的官方版本中实现。
这意味着,目前您必须使用Pyright (然而,这是没有意义的,因为您的代码将静态地键入check,然后在运行时由于缺少导入而失败)或解决办法记录在PEP 673中.:
from typing import Any, Dict, TypeVar
Self = TypeVar("Self", bound="Number")
class Number:
@classmethod
def from_number(cls: type[Self], other: Number) -> Self:
return cls(**other.dict())
https://softwareengineering.stackexchange.com/questions/435804
复制