咨询:如何解决Mypy无法识别接口子类特有方法的类型报错
解决命令模式中调用子类独有方法的Mypy类型检查问题
问题描述
我通过命令模式实现了空调控制的Python代码,但运行Mypy静态类型检查时抛出以下错误:
error: "ICommand" has no attribute "set_temperature" [attr-defined]
问题出在我需要调用仅部分ICommand实现类才具备的set_temperature方法,但不想通过#type ignore注释来规避类型检查,希望找到更优的处理方式。
实现代码
from abc import ABC, abstractmethod from typing import List class AirConditioner: """Class that represents an air conditioner""" def __init__(self, identify: str, state: bool, temperature: int): self.identify = identify self._state = state self._temperature = temperature def turn_on(self) -> None: self._state = True def turn_off(self) -> None: self._state = False def set_temperature(self, temperature: int) -> None: self._temperature = temperature def get_state(self) -> bool: return self._state def get_temperature(self) -> int: return self._temperature class ICommand(ABC): """Interface that represents a command""" @abstractmethod def execute(self) -> None: pass @abstractmethod def undo(self) -> None: pass class TurnOnAirConditioner(ICommand): """Class that represents a command to turn on an air conditioner""" def __init__(self, air_conditioner: AirConditioner): self._air_conditioner = air_conditioner def execute(self) -> None: self._air_conditioner.turn_on() def undo(self) -> None: self._air_conditioner.turn_off() class ChangeTemperatureAirConditioner(ICommand): """Class that represents a command to change the temperature of an air conditioner""" def __init__(self, air_conditioner: AirConditioner): self._air_conditioner = air_conditioner self._temperature = air_conditioner.get_temperature() self._temperature_anterior = self._temperature def set_temperature(self, temperature: int) -> None: self._temperature_anterior = self._temperature self._temperature = temperature def execute(self) -> None: self._air_conditioner.set_temperature(self._temperature) def undo(self) -> None: self._air_conditioner.set_temperature(self._temperature_anterior) class Aplicativo: """Class that represents an application that uses the command pattern to control an air conditioner""" def __init__(self) -> None: self._comandos: List[ICommand] = [] def set_comando(self, comando_app: ICommand) -> int: self._comandos.append(comando_app) return len(self._comandos) - 1 def get_command(self, comando_id: int) -> ICommand: return self._comandos[comando_id] def pressing_button(self, comando_id: int) -> None: self._comandos[comando_id].execute() if __name__ == "__main__": app = Aplicativo() my_air_conditioner = AirConditioner("Air Conditioner", False, 26) change_temperature_air = ChangeTemperatureAirConditioner(my_air_conditioner) turn_on_ar = TurnOnAirConditioner(my_air_conditioner) ID_TURN_AIR_ON = app.set_comando(turn_on_ar) ID_CHANGE_AIR_TEMPERATURE = app.set_comando(change_temperature_air) app.pressing_button(ID_TURN_AIR_ON) comando = app.get_command(ID_CHANGE_AIR_TEMPERATURE) comando.set_temperature(25)
解决方案
方案1:使用类型断言(Type Assertion)
通过typing.cast明确告知Mypy当前对象的具体类型,既满足类型检查要求,又不会影响运行时逻辑:
from typing import cast # 替换原调用代码 comando = cast(ChangeTemperatureAirConditioner, app.get_command(ID_CHANGE_AIR_TEMPERATURE)) comando.set_temperature(25)
这种方式简单直接,适合你明确知道命令类型的场景。
方案2:拆分命令接口
对命令接口进行细分,新增专门的温度控制命令接口,让类型系统更清晰:
# 新增温度命令接口 class ITemperatureCommand(ICommand): @abstractmethod def set_temperature(self, temperature: int) -> None: pass # 修改ChangeTemperatureAirConditioner的继承关系 class ChangeTemperatureAirConditioner(ITemperatureCommand): # 原有代码保持不变
之后可以在调用时通过类型判断确保安全性:
comando = app.get_command(ID_CHANGE_AIR_TEMPERATURE) if isinstance(comando, ITemperatureCommand): comando.set_temperature(25) else: raise ValueError("该命令不支持温度设置操作")
这种方式符合面向接口设计原则,能让代码结构更健壮。
方案3:使用Protocol(Python 3.8+)
利用typing.Protocol定义一个“具备温度设置能力”的协议,无需修改原有类的继承关系,灵活性更高:
from typing import Protocol class HasSetTemperature(Protocol): def set_temperature(self, temperature: int) -> None: pass # 调用时做类型检查 comando = app.get_command(ID_CHANGE_AIR_TEMPERATURE) if isinstance(comando, HasSetTemperature): comando.set_temperature(25)
Protocol采用“鸭子类型”的匹配逻辑,只要类实现了协议中的方法,就会被视为符合协议要求。
内容的提问来源于stack exchange,提问作者Magaren
相关产品推荐
相关产品推荐

