网上没有很多关于使用__round__
进行类型注释的详细信息。我已经实现了这个,但是当我运行mypy时,我仍然在第16行(在没有round
参数的情况下调用ndigits
)时出现错误:
错误:赋值中的类型不兼容(表达式的类型为“int”,变量的类型为“MyClass”)
测试通过,即在两次调用round
时,我都找回了MyClass
类型的对象。但只有当我在没有参数的情况下调用round
时,MyPy检查才会失败。
版本号:Python 3.6.5,mypy 0.641。
from typing import Any, SupportsRound, overload
class MyClass(SupportsRound['MyClass']):
def __round__(self: 'MyClass', ndigits: int = 0) -> 'MyClass':
return self
def test_tmp() -> None:
x = MyClass()
result: MyClass
result = round(x, 0)
assert type(result) == MyClass
result = round(x)
assert type(result) == MyClass
我认为这里的问题与你使用SupportsRound
的关系不大,更多的是与round
函数的定义有关。 round
函数在typeshed中定义,标准库的类型提示的存储库,具有the following signature:
@overload
def round(number: float) -> int: ...
@overload
def round(number: float, ndigits: None) -> int: ...
@overload
def round(number: float, ndigits: int) -> float: ...
@overload
def round(number: SupportsRound[_T]) -> int: ...
@overload
def round(number: SupportsRound[_T], ndigits: None) -> int: ... # type: ignore
@overload
def round(number: SupportsRound[_T], ndigits: int) -> _T: ...
请注意,当只提供一个参数或ndigits
为None时,输出始终为int
。这与round
函数在标准库中记录的行为一致:https://docs.python.org/3/library/functions.html#round
不幸的是,我没有看到一个非常干净的解决方法:我不认为implementation of SupportsRound真的与这种行为一致。
具体来说,SupportsRound可能应该被定义为类似的东西:
@runtime
class SupportsRound(Protocol[_T_co]):
@abstractmethod
@overload
def __round__(self, ndigits: None = None) -> int: ...
@abstractmethod
@overload
def __round__(self, ndigits: int) -> _T_co: ...
基本上,强迫用户处理这两种情况。
实际上改变定义可能会很复杂:实际上并没有一种干净的方式来更新与旧版本的输入模块捆绑在一起的任何旧版本的Python。
我建议在类型化问题跟踪器上提交有关此问题的问题。我个人认为你在这里发现了一个真正的不一致/错误,但这里可能存在一些细微差别我不知道,所以我认为升级它会很好。