如何在子类中输入提示工厂

问题描述 投票:0回答:1

输入提示此类子类工厂(如以下流行模式中的那些)的正确方法是什么?里氏替换原则原本只适用于实例,不适用于类,所以我不明白为什么mypy似乎限制子类中类方法的类型。

from typing import Any, Self

class A:
    def __init__(self, a: int):
        self.a = a

    @classmethod
    def create(cls, a: int, **keywords: Any) -> Self:
        return cls(a)

class B(A):
    def __init__(self, a: int, b: int):
        super().__init__(a)
        self.b = b

    @classmethod
    def create(cls, a: int, b: int, **keywords: Any) -> Self:
        return cls(a, b)

运行 mypy (1.8.0) 给出:

test_factory.py:19: error: Signature of "create" incompatible with supertype "A"  [override]
test_factory.py:19: note:      Superclass:
test_factory.py:19: note:          @classmethod
test_factory.py:19: note:          def create(cls, a: int, **keywords: Any) -> B
test_factory.py:19: note:      Subclass:
test_factory.py:19: note:          @classmethod
test_factory.py:19: note:          def create(cls, a: int, b: int, **keywords: Any) -> B
Found 1 error in 1 file (checked 1 source file)
python mypy liskov-substitution-principle
1个回答
0
投票

里氏替换原则原本只适用于实例,不适用于类,所以我不明白为什么mypy似乎限制子类中类方法的类型。

仅当实例无法访问

@classmethod
时,这才是正确的。出于实用原因,唯一允许违反 LSP 的是
__init__
/
__new__
,即使它们可以在实例上隐式访问(通过
type(self)(<...>)
)。其他任何东西,甚至是“Pythonic”
@classmethod
装饰的替代构造函数方法(如您这里的方法)都不允许违反它。

要解决此问题,您可以在基类 (

mypy Playground
) 中宽松地键入 @classmethod

from __future__ import annotations

import typing_extensions as t

if t.TYPE_CHECKING:
    import collections.abc as cx

class A:
    def __init__(self, a: int) -> None: ...
    # A positional `int` followed by any number of positional and keyword arguments
    create: t.ClassVar[cx.Callable[t.Concatenate[int, ...], A]] = classmethod(lambda cls, a, **keywords: cls(a))  # type: ignore[assignment]

class B(A):
    def __init__(self, a: int, b: int) -> None: ...
    @classmethod
    def create(cls, a: int, b: int, **keywords: t.Any) -> t.Self: ...  # OK

class C(A):
    def __init__(self, a: str, b: int) -> None: ...
    @classmethod
    def create(cls, a: str, b: int, **keywords: t.Any) -> t.Self: ...  # mypy: Incompatible types in assignment 
© www.soinside.com 2019 - 2024. All rights reserved.