我有两个抽象类,
AbstractA
和AbstractB
。 AbstractB
是通用的,其类型参数绑定到 AbstractA
。 AbstractB
还有一个工厂类方法,它返回其子类之一的实例——哪个子类是根据某个输入参数确定的。请参阅下面的最小示例。请注意,经过反复试验,我发现需要在 B_type
中添加 AbstractB.factory()
的类型提示。
from __future__ import annotations
from abc import ABC
from typing import Generic, TypeVar, Type, Any
class AbstractA(ABC):
pass
class ConcreteA1(AbstractA):
pass
class ConcreteA2(AbstractA):
pass
ATypeT = TypeVar("ATypeT", bound=AbstractA)
class AbstractB(ABC, Generic[ATypeT]):
@classmethod
def factory(cls, typeB_selector: int) -> AbstractB[Any]: # which type hint here?
B_type: Type[AbstractB[Any]] # which type hint here?
if typeB_selector == 1:
B_type= ConcreteB1
elif typeB_selector == 2:
B_type= ConcreteB2
return B_type()
class ConcreteB1(AbstractB[ConcreteA1]):
pass
class ConcreteB2(AbstractB[ConcreteA2]):
pass
我试图了解
AbstractB.factory()
和 B_type
的返回值使用什么类型提示。根据我(诚然有限)对泛型的理解,我认为合适的类型应该是AbstractB[AbstractA]
。然而,对于 strict=true
,mypy 给出了两条 B_type=...
行的错误;例如对于第一个:
Incompatible types in assignment (expression has type "type[ConcreteB1]", variable has type "type[AbstractB[AbstractA]]")
.
避免错误的唯一方法是使用
AbstractB[Any]
,如示例所示。然而,这对我来说感觉不对,因为我们知道类型参数绑定到 AbstractA
。我也尝试过AbstractB[ATypeT]
,但这似乎也是错误的,并且还会导致 mypy 错误。
AbstractB[AbstractA]
factory()
、ConcreteB1
)。关于SO有几个类似的问题(例如ConcreteB2
在factory()
上调用时)没有办法告诉类型检查器哪个子类将被退回。
抽象类不应引用其依赖项
AbstractB
上的方法引用了它的依赖者
AbstractB
和ConcreteB1
。抽象类应该代表子类的常见行为 - 通过尝试引用两个具体类,它实际上不再是常见行为,并且以某种方式需要了解它将用于的事情。希望这是有道理的。解决方案
ConcreteB2
现在工厂只引用继承的类。您可能会想,等一下,我希望它返回抽象版本。实际上,通过将 if 语句映射到类,并集实际上可以更好地描述可能的结果。事实上,你可以更进一步,这样做:
from __future__ import annotations
from abc import ABC
from typing import Generic, TypeVar
class AbstractA(ABC):
pass
class ConcreteA1(AbstractA):
pass
class ConcreteA2(AbstractA):
pass
ATypeT = TypeVar("ATypeT", bound=AbstractA)
class AbstractB(ABC, Generic[ATypeT]):
pass
class ConcreteB1(AbstractB[ConcreteA1]):
pass
class ConcreteB2(AbstractB[ConcreteA2]):
pass
def factory(typeB_selector: int) -> ConcreteB1 | ConcreteB2:
B_type: type[ConcreteB1] | type[ConcreteB2]
if typeB_selector == 1:
B_type= ConcreteB1
elif typeB_selector == 2:
B_type= ConcreteB2
else:
assert False
return B_type()
您可以创建作为工厂工作的抽象类方法,但它们不能使用从它们继承的类,例如:
int
@overload
def factory(typeB_selector: Literal[1]) -> ConcreteB1:
...
@overload
def factory(typeB_selector: Literal[2]) -> ConcreteB2:
...
def factory(typeB_selector: Literal[1,2]) -> ConcreteB1 | ConcreteB2:
match typeB_selector:
case 1:
return ConcreteB1()
case 2:
return ConcreteB2()
。