MyPy类型检查报错:子类方法在基类无对应属性的解决方案咨询
问题描述
定义基类Base及两个子类Sub1、Sub2,子类各有专属方法:
class Base: def a(self) -> None: print("a") class Sub1(Base): def b(self) -> None: print("b") class Sub2(Base): def c(self) -> None: print("c")
编写check函数接收Base类型对象,根据子类类型执行不同操作:
def check(obj: Base) -> None: obj.a() if isinstance(obj, Sub1): obj.b() elif isinstance(obj, Sub2): obj.c()
运行MyPy时触发报错:
"Base" has no attribute "b" [attr-defined]. Errors were detected by MyPy (exit code 1)
目前用cast做临时处理,但后续会新增更多子类,且该逻辑会在多个函数中复用,希望找到无需显式类型转换的更优方案。
解决方案
1. 多态重构(推荐,符合面向对象设计)
把子类专属操作抽象为基类的抽象方法,让每个子类实现自身逻辑,彻底消除类型判断:
from abc import ABC, abstractmethod class Base(ABC): def a(self) -> None: print("a") @abstractmethod def do_specific(self) -> None: # 定义子类必须实现的专属操作接口 pass class Sub1(Base): def do_specific(self) -> None: print("b") class Sub2(Base): def do_specific(self) -> None: print("c") def check(obj: Base) -> None: obj.a() obj.do_specific()
新增子类时只需实现do_specific方法,无需修改check等业务函数,完全符合开闭原则,MyPy也能完美识别类型。
2. 类型守卫(TypeGuard)
如果不想修改原有类结构,用TypeGuard让MyPy识别isinstance后的具体类型,且守卫函数可复用:
from typing import TypeGuard def is_sub1(obj: Base) -> TypeGuard[Sub1]: return isinstance(obj, Sub1) def is_sub2(obj: Base) -> TypeGuard[Sub2]: return isinstance(obj, Sub2) def check(obj: Base) -> None: obj.a() if is_sub1(obj): obj.b() elif is_sub2(obj): obj.c()
新增子类时只需添加对应的类型守卫函数,所有使用该判断的函数都能自动识别类型,不用重复写isinstance和cast。
3. 联合类型+匹配语句(Python 3.10+)
如果明确传入对象只会是指定子类,把函数参数类型改为子类联合类型,再用match-case做分支处理:
def check(obj: Sub1 | Sub2) -> None: obj.a() match obj: case Sub1(): obj.b() case Sub2(): obj.c()
MyPy能正确推断每个match分支里的对象类型,适合子类数量固定、场景明确的情况。
内容的提问来源于stack exchange,提问作者saibhaskar
相关产品推荐
相关产品推荐

