子类构造函数参数用父类类型的子类,mypy报错该如何解决?
问题描述
我定义了父类Parent与子类Child的结构:父类构造函数接收ParentType类型的参数,子类构造函数接收ParentType的子类ChildType类型的参数。代码能正常运行,但mypy会报错——访问child.a.param_2时,提示ParentType没有param_2属性,因为它将child.a的类型推断为父类中的ParentType。相关代码如下:
class ParentType: def __init__(self, param_1: int): self.param_1 = param_1 class ChildType(ParentType): def __init__(self, param_1: int, param_2: int): super().__init__(param_1=param_1) self.param_2 = param_2 class Parent: def __init__(self, a: ParentType): self.a = a class Child(Parent): def __init__(self, a: ChildType): super().__init__(a=a) child_type = ChildType(param_1=1, param_2=2) child = Child(a=child_type) print(child.a.param_1) # 正常运行 print(child.a.param_2) # 正常运行,但mypy报错:"ParentType" has no attribute "param_2"; maybe "param_1"?
正确实现方式
最优雅且可复用的解决方法是给父类Parent使用泛型,让它能关联具体的类型参数,这样子类可以明确指定自身属性的类型,让mypy正确推断。
修改后的代码如下:
from typing import Generic, TypeVar # 定义类型变量,约束为ParentType及其子类 T = TypeVar('T', bound='ParentType') class ParentType: def __init__(self, param_1: int): self.param_1 = param_1 class ChildType(ParentType): def __init__(self, param_1: int, param_2: int): super().__init__(param_1=param_1) self.param_2 = param_2 class Parent(Generic[T]): def __init__(self, a: T): self.a: T = a class Child(Parent[ChildType]): def __init__(self, a: ChildType): super().__init__(a=a) child_type = ChildType(param_1=1, param_2=2) child = Child(a=child_type) print(child.a.param_1) # 正常运行,mypy无报错 print(child.a.param_2) # 正常运行,mypy无报错
关键说明
- 导入
Generic和TypeVar,定义绑定到ParentType的类型变量T,确保只能传入ParentType或其子类作为类型参数。 - 让
Parent类继承Generic[T],并将构造函数参数a和实例属性self.a都标注为类型T,这样父类的属性类型会跟随泛型参数动态变化。 - 子类
Child继承Parent[ChildType],明确指定泛型参数为ChildType,此时mypy就能准确推断出child.a的类型是ChildType,访问param_2不会再报错。
备选方案(简单场景适用)
如果只是简单场景,也可以在子类Child中重新注解a的类型,直接覆盖父类的类型标注:
class Child(Parent): a: ChildType def __init__(self, a: ChildType): super().__init__(a=a)
不过这种方式灵活性较差,当父类逻辑复杂或需要多个子类对应不同类型时,泛型方案更合适。
内容的提问来源于stack exchange,提问作者KOB
相关产品推荐
相关产品推荐

