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
相关产品推荐
相关产品推荐

