Python 在子 class 中覆盖方法 return 的类型提示,而不重新定义方法签名
Python overriding type hint on a method's return in child class, without redefining method signature
我有一个基础 class,在方法的 return.
上有类型提示 float
在子 class 中,在不重新定义签名的情况下,我能否以某种方式将方法 return 上的类型提示更新为 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 抱怨类型不匹配:
这是因为我的 IDE 仍在使用来自 SomeClass.some_method
的类型提示。
研究
我认为解决方案可能是使用泛型,但我不确定是否有更简单的方法。
建议可以使用 instance variable annotations,但我不确定如何为 return 类型执行此操作。
你可以使用类似的东西:
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(我猜你是这个)不会显示为错误。
以下代码在 PyCharm 上运行良好。我添加了 complex
案例以使其更清楚。
我基本上将方法提取为通用 class,然后将其用作每个子 class 的混合。请格外小心使用,因为它似乎很不标准。
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")
第一个错误可以"fixed"写
RET_TYPE: ClassVar[Callable] = int
每个子class。现在,错误减少到
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,那其实并不重要。
好吧,我可以尝试并结合 and 的答案得出一个解决方案,其中:
- 我的IDE(PyCharm)可以正确推断出return类型
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"
)
我有一个基础 class,在方法的 return.
上有类型提示float
在子 class 中,在不重新定义签名的情况下,我能否以某种方式将方法 return 上的类型提示更新为 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 抱怨类型不匹配:
这是因为我的 IDE 仍在使用来自 SomeClass.some_method
的类型提示。
研究
我认为解决方案可能是使用泛型,但我不确定是否有更简单的方法。
建议可以使用 instance variable annotations,但我不确定如何为 return 类型执行此操作。
你可以使用类似的东西:
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(我猜你是这个)不会显示为错误。
以下代码在 PyCharm 上运行良好。我添加了 complex
案例以使其更清楚。
我基本上将方法提取为通用 class,然后将其用作每个子 class 的混合。请格外小心使用,因为它似乎很不标准。
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")
第一个错误可以"fixed"写
RET_TYPE: ClassVar[Callable] = int
每个子class。现在,错误减少到
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,那其实并不重要。
好吧,我可以尝试并结合
- 我的IDE(PyCharm)可以正确推断出return类型
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"
)