Python 3类型提示,用于返回子类实例的基类上的工厂方法(Python 3 type hint for a factory method on a base class returning a c

编程入门 行业动态 更新时间:2024-10-13 10:22:56
Python 3类型提示,用于返回子类实例的基类上的工厂方法(Python 3 type hint for a factory method on a base class returning a child class instance)

假设我在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 ++并且所有......

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

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()

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.


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']): ...

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

最满意答案

确实有可能:该功能被称为带有Generic Self的TypeVar (虽然这有点误导,因为在这种情况下我们将这用于类方法)。 我相信它的行为大致相当于你所链接的“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的注释。

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': ...

Note that:

We don't need to make the class itself generic since we only need a generic function Setting the TypeVar's bound to 'Base' is strictly speaking optional, but is probably a good idea: this way, the callers of your base class/subclasses will at least be able to call methods defined in the base class even if you don't know exactly which subclass you're dealing with. We can omit the annotation on cls for the child definition.

更多推荐

本文发布于:2023-08-03 09:14:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1385421.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:子类   实例   工厂   提示   类型

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!