假设我在Base中有两个类Base和Child.工厂方法调用另一个类方法,它可以被Base的子类覆盖.
class Base(object):
@classmethod
def create(cls, *args: Tuple) -> 'Base':
value = cls._prepare(*args)
return cls(value)
@classmethod
def _prepare(cls, *args: Tuple) -> Any:
return args[0] if args else None
def __init__(self, value: Any) -> None:
self.value = value
class Child(Base):
@classmethod
def _prepare(cls, *args: Tuple) -> Any:
return args[1] if len(args) > 1 else None
def method_not_present_on_base(self) -> None:
pass
有没有办法注释Base.create,以便静态类型检查器可以推断Base.create()返回Base的实例,Child.create()返回Child的实例,以便以下示例将传递静态分析?
base = Base.create(1)
child = Child.create(2, 3)
child.method_not_present_on_base()
在上面的示例中,静态类型检查器会正确地抱怨method_not_present_on_base在Base类中不存在.
我想过将Base转换为泛型类,让子类将自己指定为类型参数,即将CRTP引入Python.
T = TypeVar('T')
class Base(Generic[T]):
@classmethod
def create(cls, *args: Tuple) -> T: ...
class Child(Base['Child']): ...
但是这种感觉相当单一,而CRTP来自C和所有……
解决方法:
这确实是可能的:该功能被称为TypeVar with Generic Self(虽然这有点误导,因为在这种情况下我们将这用于类方法).我相信它与你所链接的“CRTP”技术大致相同(尽管我不是C专家所以不能肯定地说).
在任何情况下,您都会声明您的基类和子类,如下所示:
from typing import TypeVar, Type, Tuple
T = TypeVar('T', bound='Base')
class Base:
@classmethod
def create(cls: Type[T], *args: Tuple[Any]) -> T: ...
class Child(Base):
@classmethod
def create(cls, *args: Tuple[Any]) -> 'Child': ...
注意:
>我们不需要使类本身通用,因为我们只需要一个泛型函数
>将TypeVar绑定到’Base’严格来说是可选的,但这可能是个好主意:这样,基类/子类的调用者至少能够调用基类中定义的方法,即使你没有’我确切地知道你正在处理哪个子类.
>我们可以省略子定义的cls上的注释.
标签:python,python-3-x,type-hinting