Python 3类型提示,用于返回子类实例的基类上的工厂方法

时间:2017-09-01 20:24:27

标签: python python-3.x type-hinting

假设我在Base中有两个类ChildBase的工厂方法。工厂方法调用另一个类方法,可以被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()返回{{1}的实例,以便以下示例将传递静态分析?

Child

在上面的示例中,静态类型检查器会理所当然地抱怨base = Base.create(1) child = Child.create(2, 3) child.method_not_present_on_base() 类上没有method_not_present_on_base

我考虑过将Base转换为泛型类,并让子类将自己指定为类型参数,即将CRTP引入Python。

Base

但是CRTP来自C ++并且所有人都感到相当不熟悉......

1 个答案:

答案 0 :(得分:5)

确实有可能:该功能被称为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': ...

请注意:

  1. 我们不需要使类本身具有通用性,因为我们只需要一个通用函数
  2. 将TypeVar设置为' Base'严格来说是可选的,但可能是一个好主意:这样,你的基类/子类的调用者至少能够调用基类中定义的方法,即使你不确切知道你的哪个子类#&# 39;重新处理。
  3. 我们可以在cls上省略子注释的注释。