代码之家  ›  专栏  ›  技术社区  ›  Intrastellar Explorer

Python重写子类中方法返回的类型提示,而不重新定义方法签名

  •  0
  • Intrastellar Explorer  · 技术社区  · 6 年前

    我有一个基类,类型为 float 方法返回时。

    在子类中,在不重新定义签名的情况下,我是否可以在方法返回时以某种方式更新类型提示 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 expected type float

    之所以发生这种情况,是因为我的IDE仍在使用 SomeClass.some_method .


    研究

    我认为解决办法 可以 可以使用泛型,但我不确定是否有更简单的方法。

    Python: how to override type hint on an instance attribute in a subclass?

    建议使用 instance variable annotations ,但我不确定如何为返回类型执行此操作。

    0 回复  |  直到 6 年前
        1
  •  2
  •   Kevin Languasco    6 年前

    以下代码在PyCharm上运行良好。我添加了 complex 让事情变得更清楚。

    我基本上是将该方法提取到一个泛型类,然后将其用作每个子类的混合。请格外小心使用,因为它似乎不太标准。

    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,那就不重要了。

        2
  •  0
  •   Anton Pomieshchenko BOY.py    6 年前

    你可以这样使用:

    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方法,某些_方法IDE将显示通用提示。至少pycharm(我想你是这么认为的)没有把它显示为错误。

        3
  •  0
  •   Intrastellar Explorer    6 年前

    好的,所以我能够把@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"
        )
    
    推荐文章