Python 方法重写 - 派生类中的参数比基类中的参数更具体

mal*_*sen 5 python methods inheritance overriding python-3.x

假设我想创建一个名为 的抽象基类Document。我希望类型检查器保证其所有子类都实现一个名为 的类方法from_paragraphs,该方法从一系列Paragraph对象构造一个文档。然而, aLegalDocument只能从LegalParagraph对象构造,而AcademicDocument- 只能从AcademicParagraph对象构造。

我的直觉是这样做:

from abc import ABC, abstractmethod
from typing import Sequence


class Document(ABC):
    @classmethod
    @abstractmethod
    def from_paragraphs(cls, paragraphs: Sequence["Paragraph"]):
        pass


class LegalDocument(Document):
    @classmethod
    def from_paragraphs(cls, paragraphs: Sequence["LegalParagraph"]):
        return  # some logic here...


class AcademicDocument(Document):
    @classmethod
    def from_paragraphs(cls, paragraphs: Sequence["AcademicParagraph"]):
        return  # some logic here...


class Paragraph:
    text: str


class LegalParagraph(Paragraph):
    pass


class AcademicParagraph(Paragraph):
    pass
Run Code Online (Sandbox Code Playgroud)

然而,Pyright 对此有所抱怨,因为from_paragraphs在派生类上违反了里氏替换原则。如何确保每个派生类都实现某种from_paragraphs类型?Paragraph

mal*_*sen 0

事实证明,这可以使用泛型来解决:

from abc import ABC, abstractmethod
from typing import Generic, Sequence, TypeVar

ParagraphType = TypeVar("ParagraphType", bound="Paragraph")


class Document(ABC, Generic[ParagraphType]):
    @classmethod
    @abstractmethod
    def from_paragraphs(cls, paragraphs: Sequence[ParagraphType]):
        pass


class LegalDocument(Document["LegalParagraph"]):
    @classmethod
    def from_paragraphs(cls, paragraphs):
        return  # some logic here...


class AcademicDocument(Document["AcademicParagraph"]):
    @classmethod
    def from_paragraphs(cls, paragraphs):
        return  # some logic here...


class Paragraph:
    text: str


class LegalParagraph(Paragraph):
    pass


class AcademicParagraph(Paragraph):
    pass
Run Code Online (Sandbox Code Playgroud)

bound="Paragraph"保证 ParagraphType 代表 (的子类) Paragraph,但派生类不应实现from_paragraphs所有段落类型,而仅实现它们选择的一种。类型检查器还会自动找出 的参数类型,paragraphsLegalDocument.from_paragraphs我节省了一些工作:)