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

Python父类实现返回子类类型的通用check方法以避免重复代码

问题描述

我在Python 3中有一个包含多个子类的父类Parent,当前类层级结构如下:

class Parent:
    @classmethod
    def check(cls, obj: "Parent"):
        pass

class Child1(Parent):    
    def __init__(self, x):
        self.x = x

    @classmethod
    def check(cls, obj: Parent) -> "Child1":
        if cls == obj.__class__:
            return obj
        else:
            raise TypeError("Bad type received.")

class Child2(Parent):
    def __init__(self, y):
        self.y = y

    @classmethod
    def check(cls, obj: Parent) -> "Child2":
        if cls == obj.__class__:
            return obj
        else:
            raise TypeError("Bad type received.")

# ... 更多子类 ...

还有一套使用这些类的Runnable层级结构:

from abc import abstractmethod, ABC

class Runnable(ABC):
    @abstractmethod
    def run(self) -> Parent:
        pass

class Thing1(Runnable):
    def run(self) -> Parent:
        # ... 生成Child1的逻辑 ...

class Thing2(Runnable):
    def run(self) -> Parent:
        # ... 生成Child2的逻辑 ...

调用Thing1.run()后需要访问返回对象的x字段,Python允许该操作但类型不安全。check方法的作用是作为断言和类型转换,让Child1.check(Thing.run()).x的操作类型安全,但可能抛出错误。

但各子类的check方法实现完全相同,仅返回类型不同,存在大量重复代码。想知道能否在Python中实现如下父类定义,避免重复:

class Parent:
    @classmethod
    def check(cls, obj: Parent) -> cls:   # 这行返回类型在实际Python中不允许
        if cls == obj.__class__:
            return obj
        else:
            raise TypeError("Bad type received.")
解决方案

方法1:使用泛型父类(推荐)

通过TypeVar和泛型可以让类型检查器正确识别返回的具体子类类型,同时在父类中统一实现check方法:

from typing import TypeVar, Type, Generic

# 定义绑定到Parent及其子类的泛型变量
T = TypeVar('T', bound='Parent')

class Parent(Generic[T]):
    @classmethod
    def check(cls: Type[T], obj: "Parent") -> T:
        if cls == obj.__class__:
            return obj
        else:
            raise TypeError("Bad type received.")

class Child1(Parent['Child1']):    
    def __init__(self, x):
        self.x = x

class Child2(Parent['Child2']):
    def __init__(self, y):
        self.y = y

每个子类只需继承Parent并传入自身作为泛型参数,无需重复实现check,类型检查器能正确推断返回类型,比如Child1.check(thing1.run()).x会被识别为合法操作。

方法2:字符串形式的返回类型注解

如果不想用泛型,可以在父类中用'cls'作为字符串形式的返回类型注解,主流类型检查器(如MyPy、Pyright)都能正确解析:

class Parent:
    @classmethod
    def check(cls, obj: "Parent") -> 'cls':
        if cls == obj.__class__:
            return obj
        else:
            raise TypeError("Bad type received.")

class Child1(Parent):    
    def __init__(self, x):
        self.x = x

class Child2(Parent):
    def __init__(self, y):
        self.y = y

这种写法更简洁,子类无需重写方法就能获得正确的类型提示。

方法3:使用cast辅助类型转换

若要兼容旧版类型检查器,可在父类方法中用cast明确转换类型:

from typing import cast, Type

class Parent:
    @classmethod
    def check(cls: Type['Parent'], obj: "Parent") -> 'Parent':
        if cls == obj.__class__:
            return cast(cls, obj)
        else:
            raise TypeError("Bad type received.")

不过这种方式在子类调用时,类型检查器可能需要额外提示,不如前两种直观。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 03:30:40