You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

父类工厂方法返回子类实例,Self类型注解为何遭Mypy报错?

问题分析与解决方案

你的问题核心是对Self类型的理解出现了偏差,这不是Mypy或typing_extensions的问题。

为什么Self会报错?

Self类型的本质是调用该类方法的那个具体类的实例类型,而不是父类Parent的类型。举个例子:

  • 如果调用ChildA.make(1),Self代表的是ChildA类型,但你的方法可能返回ChildB,这就和Self要求的类型不匹配;
  • 同理,调用ChildB.make(-1)时,Self是ChildB类型,但方法可能返回ChildA,同样不符合类型要求。

Mypy正是因为检测到这种潜在的类型不匹配,才抛出了报错。

为什么直接赋值给Parent类型没问题?

你提到的这段代码:

param = 1
foo: Parent = ChildA() if param > 0 else ChildB()

是典型的父类类型接收子类实例,这符合Python的子类型赋值规则——所有子类都是Parent的子类型,因此Mypy允许这种赋值。但这和Self的逻辑完全不同:Parent是所有子类的通用父类型,而Self是具体的调用类类型,二者不能划等号。

正确的类型注解方案

  1. 直接使用Parent作为返回类型(你的临时方案其实是合理的)
    因为你的工厂方法返回的是Parent的任意子类实例,用父类型Parent作为返回注解完全准确,也最简洁:

    class Parent:
        @classmethod
        def make(cls, param: int) -> Parent:
            if param > 0:
                return ChildA()
            else:
                return ChildB()
    
    class ChildA(Parent):
        pass
    
    class ChildB(Parent):
        pass
    
  2. 用TypeVar实现更灵活的类型绑定(可选)
    如果想避免重复写Parent类名,可以结合TypeVar和bound参数,但本质上和用Parent做返回类型效果一致:

    from typing_extensions import TypeVar
    
    P = TypeVar('P', bound='Parent')
    
    class Parent:
        @classmethod
        def make(cls: type[P], param: int) -> Parent:
            if param > 0:
                return ChildA()
            else:
                return ChildB()
    
    class ChildA(Parent):
        pass
    
    class ChildB(Parent):
        pass
    

补充:什么时候该用Self?

Self适合工厂方法返回当前调用类的实例的场景,比如根据参数创建调用类本身的对象:

from typing_extensions import Self

class Parent:
    def __init__(self, param: int):
        self.param = param

    @classmethod
    def make(cls, param: int) -> Self:
        return cls(param)  # 返回当前调用类的实例,此时Self是正确注解

class ChildA(Parent):
    pass

class ChildB(Parent):
    pass

这时调用ChildA.make(1)会返回ChildA实例,ChildB.make(-1)返回ChildB实例,完全符合Self的类型要求,Mypy也不会报错。

内容的提问来源于stack exchange,提问作者Florian Brucker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 00:50:10