使用以下代码:
import abc
class ABCParent(metaclass=abc.ABCMeta):
def __init__(self, a: str, sibling_type: type[ABCParent]) -> None:
self.a = a
self._sibling_type = sibling_type
def new_sibling(self, a: str) -> ABCParent:
return self._sibling_type(a)
class ChildA(ABCParent):
def __init__(self, a: str) -> None:
super().__init__(a, ChildB)
class ChildB(ABCParent):
def __init__(self, a: str) -> None:
super().__init__(a, ChildA)
在严格模式下使用 mypy 时出现以下键入错误。
src/demo_problem.py: note: In member "new_sibling" of class "ABCParent":
src/demo_problem.py:10:16:10:36: error: Missing positional argument "sibling_type" in call to "ABCParent" [call-arg]
return self._sibling_type(a)
这是完全合乎逻辑的,因为
ABCParent
请求 sibling_type
参数。但就我而言, self._sibling_type
将是子类之一,它会重写构造函数,这样就不需要 sibling_type
了。
我能够通过使用子类的
typing.Union
来解决这个问题:
import abc
import typing
TConcreteChild: typing.TypeAlias = typing.Union["ChildA", "ChildB"]
class ABCParent(metaclass=abc.ABCMeta):
def __init__(self, a: str, sibling_type: type[TConcreteChild]) -> None:
...
但我不喜欢这个,因为:
TConcreteChild
),否则毫无用处ChildA | ChildB
语法)。我怎样才能更干净地做到这一点,同时仍然进行严格的类型检查?
因此,我认为一种可能有效(并不总是)的方法是,不要将
._sibling_type
设为类型,而是使用泛型类将其设为泛型可调用,
import abc
import typing
P = typing.TypeVar("P", bound="ABCParent")
class ABCParent(typing.Generic[P], metaclass=abc.ABCMeta):
def __init__(self, a: str, sibling_type: typing.Callable[[str], P]) -> None:
self.a = a
self._sibling_type = sibling_type
def new_sibling(self, a: str) -> P:
return self._sibling_type(a)
class ChildA(ABCParent["ChildB"]):
def __init__(self, a: str) -> None:
super().__init__(a, ChildB)
class ChildB(ABCParent["ChildA"]):
def __init__(self, a: str) -> None:
super().__init__(a, ChildA)
类型是可调用的。根据我对
mypy
的经验,这样对待它们效果更好。