抽象方法返回 self 类型

MSH*_*MSH 3 python inheritance mypy python-typing

我的 mypy 遇到一些问题。

我有一个抽象类和一个继承它的类:

from __future__ import annotations

from abc import abstractmethod, ABC
from typing import Union


class Base(ABC):
    @abstractmethod
    def the_method(self, a_class: Union[Base, float, int]) -> None:
        ...

    @abstractmethod
    def other_method(self) -> None:
        ...


class MyClass(Base):
    def __init__(self, something: str = "Hello") -> None:
        self.something = something

    def the_method(self, a_class: Union[MyClass, float, int]) -> None:
        print(a_class)

    def other_method(self) -> None:
        print(self.something)
Run Code Online (Sandbox Code Playgroud)

我知道里氏替换原则。然而MyClass是 的一种类型,Base因为它继承自它。但mypy仍然会引发错误:

from __future__ import annotations

from abc import abstractmethod, ABC
from typing import Union


class Base(ABC):
    @abstractmethod
    def the_method(self, a_class: Union[Base, float, int]) -> None:
        ...

    @abstractmethod
    def other_method(self) -> None:
        ...


class MyClass(Base):
    def __init__(self, something: str = "Hello") -> None:
        self.something = something

    def the_method(self, a_class: Union[MyClass, float, int]) -> None:
        print(a_class)

    def other_method(self) -> None:
        print(self.something)
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?

Joe*_*ley 5

Base.the_method接受,因此子类至少Base也需要接受。如果我有,那应该被接受。目前,尚未被 接受。 Baseclass Foo(Base)MyClass.the_method

对于方法参数和返回类型,里氏替换的工作方向相反 - 超类可以用于参数,子类可以用于返回类型。

你有很多选择。例如,您可以MyClass.the_method接受Base

class MyClass(Base):
    def the_method(self, a_class: Union[Base, float, int]) -> None:
        ...
Run Code Online (Sandbox Code Playgroud)

或者(评论中讨论了下一个示例是否也违反了 LSP ...无论哪种方式,mypy 对此都过于宽容)更改Base为接受封闭类的实例

from typing import Self

class Base(ABC):
    @abstractmethod
    def the_method(self, a_class: Union[Self, float, int]) -> None:
        ...
Run Code Online (Sandbox Code Playgroud)

  • @zvone实际上我不相信这确实违反了LSP,因为`Base`并没有说你可以用`Base`调用`the_method`,它说你可以用你不再知道的任何类型来调用它,将其转换为“Base”,因此您只能调用“x.the_method(x)” (2认同)