返回子类实例的基类工厂方法的 Python 3 类型提示 [英] Python 3 type hint for a factory method on a base class returning a child class instance

查看:52
本文介绍了返回子类实例的基类工厂方法的 Python 3 类型提示的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有两个类 BaseChild,在 Base 中有一个工厂方法.工厂方法调用另一个可能被 Base 的子类覆盖的类方法.

Let's say I have two classes Base and Child with a factory method in Base. The factory method calls another classmethod which may be overriden by Base's child classes.

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 的实例code> 和 Child.create() 返回了一个 Child 的实例,这样下面的例子就可以通过静态分析了吗?

Is there a way to annotate Base.create so that a static type checker could infer that Base.create() returned an instance of Base and Child.create() returned an instance of Child, so that the following example would pass static analysis?

base = Base.create(1)
child = Child.create(2, 3)
child.method_not_present_on_base()

在上面的例子中,静态类型检查器会理所当然地抱怨 method_not_present_on_base 没有出现在 Base 类中.

In the above example a static type checker would rightfully complain that the method_not_present_on_base is, well, not present on the Base class.

我考虑将 Base 变成一个泛型类,并让子类将自己指定为类型参数,即引入 CRTP 到 Python.

I thought about turning Base into a generic class and having the child classes specify themselves as type arguments, i.e. bringing the CRTP to Python.

T = TypeVar('T')

class Base(Generic[T]):
    @classmethod
    def create(cls, *args: Tuple) -> T: ...

class Child(Base['Child']): ...

但是对于来自 C++ 的 CRTP 和所有的东西,这感觉相当不合逻辑...

But this feels rather unpythonic with CRTP coming from C++ and all...

推荐答案

确实有可能:该功能名为 TypeVar with Generic Self(虽然这有点误导,因为在这种情况下我们将它用于类方法).我相信它的行为与您链接的CRTP"技术大致相同(尽管我不是 C++ 专家,因此不能肯定).

It is indeed possible: the feature is called TypeVar with Generic Self (though this is slightly misleading because we're using this for a class method in this case). I believe it behaves roughly equivalently to the "CRTP" technique you linked to (though I'm not a C++ expert so can't say for certain).

在任何情况下,您都可以像这样声明您的基类和子类:

In any case, you would declare your base and child classes like so:

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' 严格来说是可选的,但可能是一个好主意:这样,您的基类/子类的调用者至少能够调用基类中定义的方法,即使您不知道您正在处理哪个子类.
  3. 我们可以省略子定义的 cls 注释.

这篇关于返回子类实例的基类工厂方法的 Python 3 类型提示的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆