簡體   English   中英

Python 子 class 中方法返回的覆蓋類型提示,沒有重新定義方法簽名

[英]Python overriding type hint on a method's return in child class, without redefining method signature

我有一個基礎 class 在方法返回時帶有float類型提示。

在子 class 中,在不重新定義簽名的情況下,我可以以某種方式將方法返回的類型提示更新為int嗎?


示例代碼

#!/usr/bin/env python3.6


class SomeClass:
    """This class's some_method will return float."""

    RET_TYPE = float

    def some_method(self, some_input: str) -> float:
        return self.RET_TYPE(some_input)


class SomeChildClass(SomeClass):
    """This class's some_method will return int."""

    RET_TYPE = int


if __name__ == "__main__":
    ret: int = SomeChildClass().some_method("42"). # 
    ret2: float = SomeChildClass().some_method("42")

我的 IDE 抱怨類型不匹配:

pycharm 預期類型浮點數

發生這種情況是因為我的 IDE 仍在使用SomeClass.some_method的類型提示。


研究

我認為解決方案可能是使用 generics,但我不確定是否有更簡單的方法。

Python:如何覆蓋子類中實例屬性的類型提示?

建議可能使用實例變量注釋,但我不確定如何為返回類型執行此操作。

以下代碼在 PyCharm 上運行良好。 我添加了complex的案例以使其更清晰。

我基本上將該方法提取到通用 class 中,然后將其用作每個子類的 mixin。 請格外小心使用,因為它似乎相當不標准。

from typing import ClassVar, Generic, TypeVar, Callable


S = TypeVar('S', bound=complex)


class SomeMethodImplementor(Generic[S]):
    RET_TYPE: ClassVar[Callable]

    def some_method(self, some_input: str) -> S:
        return self.__class__.RET_TYPE(some_input)


class SomeClass(SomeMethodImplementor[complex]):
    RET_TYPE = complex


class SomeChildClass(SomeClass, SomeMethodImplementor[float]):
    RET_TYPE = float


class OtherChildClass(SomeChildClass, SomeMethodImplementor[int]):
    RET_TYPE = int


if __name__ == "__main__":
    ret: complex = SomeClass().some_method("42")
    ret2: float = SomeChildClass().some_method("42")
    ret3: int = OtherChildClass().some_method("42")
    print(ret, type(ret), ret2, type(ret2), ret3, type(ret3))

例如,如果您將ret2: float更改為ret2: int ,它將正確顯示類型錯誤。

可悲的是, mypy在這種情況下確實顯示錯誤(版本 0.770),

otherhint.py:20: error: Incompatible types in assignment (expression has type "Type[float]", base class "SomeClass" defined the type as "Type[complex]")
otherhint.py:24: error: Incompatible types in assignment (expression has type "Type[int]", base class "SomeClass" defined the type as "Type[complex]")
otherhint.py:29: error: Incompatible types in assignment (expression has type "complex", variable has type "float")
otherhint.py:30: error: Incompatible types in assignment (expression has type "complex", variable has type "int")

第一個錯誤可以通過編寫來“修復”

    RET_TYPE: ClassVar[Callable] = int

對於每個子類。 現在,錯誤減少到

otherhint.py:29: error: Incompatible types in assignment (expression has type "complex", variable has type "float")
otherhint.py:30: error: Incompatible types in assignment (expression has type "complex", variable has type "int")

這與我們想要的恰恰相反,但如果你只關心 PyCharm,那也沒關系。

你可以使用類似的東西:

from typing import TypeVar, Generic


T = TypeVar('T', float, int) # types you support


class SomeClass(Generic[T]):
    """This class's some_method will return float."""

    RET_TYPE = float

    def some_method(self, some_input: str) -> T:
        return self.RET_TYPE(some_input)


class SomeChildClass(SomeClass[int]):
    """This class's some_method will return int."""

    RET_TYPE = int


if __name__ == "__main__":
    ret: int = SomeChildClass().some_method("42")
    ret2: float = SomeChildClass().some_method("42")

但是有一個問題。 那我不知道怎么解決。 對於 SomeChildClass 方法 some_method IDE 將顯示通用提示。 至少 pycharm(我想你是這個)不會將其顯示為錯誤。

好的,所以我可以嘗試並結合@AntonPomieshcheko 和@KevinLanguasco 的答案來提出一個解決方案,其中:

  • 我的 IDE (PyCharm) 可以正確推斷返回類型
  • 如果類型不匹配, mypy報告
  • 運行時不會出錯,即使類型提示指示不匹配

這正是我想要的行為。 非常感謝大家:)

#!/usr/bin/env python3

from typing import TypeVar, Generic, ClassVar, Callable


T = TypeVar("T", float, int)  # types supported


class SomeBaseClass(Generic[T]):
    """This base class's some_method will return a supported type."""

    RET_TYPE: ClassVar[Callable]

    def some_method(self, some_input: str) -> T:
        return self.RET_TYPE(some_input)


class SomeChildClass1(SomeBaseClass[float]):
    """This child class's some_method will return a float."""

    RET_TYPE = float


class SomeChildClass2(SomeBaseClass[int]):
    """This child class's some_method will return an int."""

    RET_TYPE = int


class SomeChildClass3(SomeBaseClass[complex]):
    """This child class's some_method will return a complex."""

    RET_TYPE = complex


if __name__ == "__main__":
    some_class_1_ret: float = SomeChildClass1().some_method("42")
    some_class_2_ret: int = SomeChildClass2().some_method("42")

    # PyCharm can infer this return is a complex.  However, running mypy on
    # this will report (this is desirable to me):
    # error: Value of type variable "T" of "SomeBaseClass" cannot be "complex"
    some_class_3_ret = SomeChildClass3().some_method("42")

    print(
        f"some_class_1_ret = {some_class_1_ret} of type {type(some_class_1_ret)}\n"
        f"some_class_2_ret = {some_class_2_ret} of type {type(some_class_2_ret)}\n"
        f"some_class_3_ret = {some_class_3_ret} of type {type(some_class_3_ret)}\n"
    )

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM