mypy无法识别类的一个方法。

huangapple go评论65阅读模式
英文:

mypy does not recognize a method of a class

问题

The error you're encountering is because the "ICommand" interface doesn't define a "set_temperature" method, but your "ChangeTemperatureAirConditioner" class, which implements "ICommand," has a "set_temperature" method. You can handle this situation by making the "set_temperature" method optional in the interface using Python's Optional from the typing module.

Here's the modified code for "ICommand" and "ChangeTemperatureAirConditioner":

from abc import ABC, abstractmethod
from typing import List, Optional

class ICommand(ABC):
    """Interface that represents a command"""

    @abstractmethod
    def execute(self) -> None:
        pass

    @abstractmethod
    def undo(self) -> None:
        pass

    @abstractmethod
    def set_temperature(self, temperature: int) -> None:
        pass

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)

By adding set_temperature to the ICommand interface, you avoid the error, and you can implement the method in classes where it's needed and leave it unimplemented in classes where it's not applicable.

英文:
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)

When I run the code above, mypy brings me the following alert:

error: "ICommand" has no attribute "set_temperature" [attr-defined]

How do I do it when I need to call a method, but not every class that implements the ICommand interface has this method?

I tried to comment lines with #type ignore, but I would like to know a better way to handle this problem

答案1

得分: 1

考虑以下两行:

comando = app.get_command(ID_CHANGE_AIR_TEMPERATURE)
comando.set_temperature(25)

即使我们忽略您将变量命名为`ID_CHANGE_AIR_TEMPERATURE`的事实,从您的使用方式来看,它仍意味着您知道`comando`的类型是`ChangeTemperatureAirConditioner`(您在其上调用`.set_temperature(25)`)。

一种解决方法是从`ICommand`转换为`ChangeTemperatureAirConditioner`:

from typing import cast
comando = cast(ChangeTemperatureAirConditioner, app.get_command(ID_CHANGE_AIR_TEMPERATURE))

英文:

Consider these two lines:

comando = app.get_command(ID_CHANGE_AIR_TEMPERATURE)
comando.set_temperature(25)

Even if we ignore the fact that you call your variable ID_CHANGE_AIR_TEMPERATURE, it still implies that you know comando is of type ChangeTemperatureAirConditioner from how you use it (you call .set_temperature(25) on it).

One solution would then be to cast from ICommand to ChangeTemperatureAirConditioner:

from typing import cast
comando = cast(ChangeTemperatureAirConditioner, app.get_command(ID_CHANGE_AIR_TEMPERATURE))

答案2

得分: 0

Sure, here's the translated content:

遵循 mypy 指南:

> 在您的代码对于 mypy 来说太过神秘以至于无法理解的情况下,您可以通过明确地将变量或参数的类型设置为 Any 来使其动态类型化。

来源:https://mypy.readthedocs.io/en/stable/dynamic_typing.html

解决这个问题的最佳方法是更改 Aplicativo 类中的 get_command 方法:

from abc import ABC, abstractmethod
from typing import List, Any

...

class Aplicativo:
    """代表一个使用命令模式来控制空调的应用程序的类"""

    ...

    def get_command(self, comando_id: int) -> Any:
        return self._comandos[comando_id]

    ...

解决了 mypy 的问题。

英文:

Following mypy guidelines:

> In cases where your code is too magical for mypy to understand, you
> can make a variable or parameter dynamically typed by explicitly
> giving it the type Any

source: https://mypy.readthedocs.io/en/stable/dynamic_typing.html

The best way to solve this problem is change get_command method, in Aplicativo class to:

from abc import ABC, abstractmethod
from typing import List, Any
    
    ...

class Aplicativo:
        """Class that represents an application that uses the command pattern to control an air conditioner"""
    
        ...

        def get_command(self, comando_id: int) -> Any:
            return self._comandos[comando_id]

        ...

Solving the issue with mypy

huangapple
  • 本文由 发表于 2023年3月21日 03:24:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/75794492.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定