我的 Python 项目中面临类型安全问题,涉及抽象基类 (
AbstractClass
) 及其具体子类(ConcreteClassA
和 ConcreteClassB
)。每个具体类都实现 AbstractClass
中的抽象方法,但我正在努力加强两个具体类之间的类型安全。
这是我的代码结构的简化版本:
class AbstractClass(ABC):
class rational:
pass
@dataclass
class Frac(rational):
value1: int
value2: int
@dataclass
class Whole(rational):
value: int
@classmethod
@abstractmethod
def foo(cls, x: int, y: int) -> rational:
pass
@classmethod
@abstractmethod
def boo(cls, r: rational) -> str:
pass
class ConcreteClassA(AbstractClass):
@classmethod
def foo(cls, x: int, y: int) -> AbstractClass.rational:
...
@classmethod
def boo(cls, r: AbstractClass.rational) -> str:
...
@staticmethod
def __reduce(r: AbstractClass.rational) -> AbstractClass.Whole | AbstractClass.Frac:
match r:
case Rational1.Whole(_):
return r
case Rational1.Frac(x, y):
if x == 0:
return AbstractClass.Whole(0)
else:
d = Rational1.foo(abs(x), y)
if d == y:
return AbstractClass.Whole(x // d)
else:
return AbstractClass.Frac(x // d, y // d)
case _:
raise Exception("ERRR")
class ConcreteClassB(AbstractClass):
@classmethod
def foo(cls, x: int, y: int) -> AbstractClass.rational:
...
@classmethod
def boo(cls, r: AbstractClass.rational) -> str:
...
@staticmethod
def __reduce(r: AbstractClass.rational) -> AbstractClass.Whole | AbstractClass.Frac:
match r:
case Rational1.Whole(_):
return r
case Rational1.Frac(x, y):
if x == 0:
return AbstractClass.Whole(0)
else:
d = Rational1.foo(abs(x), y)
if d == y:
return AbstractClass.Whole(x // d)
else:
return AbstractClass.Frac(x // d, y // d)
case _:
raise Exception("ERRR")
a = ConcreteClassA.boo(ConcreteClassB.foo(9, -6))
b = ConcreteClassB.boo(ConcreteClassA.foo(9, -6))
编辑:正如您所看到的,有静态方法和其他相互调用的方法,并且我认为实现不会允许 Self 出现在抽象类中。
尽管有这种结构,但在
ConcreteClassA
和 ConcreteClassB
之间没有执行类型安全。例如,像 ConcreteClassA.boo
这样带有 ConcreteClassB.foo
参数的调用不会导致任何错误,这是不希望的。
我正在使用 Pyright 进行类型检查。如何修改代码以确保在
ConcreteClassA
和 ConcreteClassB
之间传递数据时产生错误?请注意,我不喜欢将泛型、类型别名或其他构造直接添加到主类中。
还有
@dataclass
内部的实现 AbstractClass
,就跟着它吧。
我相信您正在寻找
Self
。 Self
用于表示当前类的实例 - 见下文:
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Self
class AbstractClass(ABC):
class rational:
pass
@dataclass
class Frac(rational):
value1: int
value2: int
@dataclass
class Whole(rational):
value: int
@classmethod
@abstractmethod
def foo(cls, x: int, y: int) -> Self:
pass
@classmethod
@abstractmethod
def boo(cls, r: Self) -> str:
pass
class ConcreteClassA(AbstractClass):
@classmethod
def foo(cls, x: int, y: int) -> Self:
...
@classmethod
def boo(cls, r: Self) -> str:
...
class ConcreteClassB(AbstractClass):
@classmethod
def foo(cls, x: int, y: int) -> Self:
...
@classmethod
def boo(cls, r: Self) -> str:
...
a = ConcreteClassA.boo(ConcreteClassB.foo(9, -6)) # error
b = ConcreteClassB.boo(ConcreteClassA.foo(9, -6)) # error
a = ConcreteClassA.boo(ConcreteClassA.foo(9, -6)) # no error
b = ConcreteClassB.boo(ConcreteClassB.foo(9, -6)) # no error