创建与 int 兼容的新 int 子类型 mypy

Create new `int` subtype mypy compatible with `int`

我想扩展 int 类型 mypy 仍然将其识别为 int。例如:

class u8(int):
    _size_bits = 8
    _struct_format: str = 'B'

    def validate(self):
        "Internal function. Mypy shouldn't care about it"
        return 0 <= int(self) <= 255

所以我可以使用

i: u8 = 10

但是mypy给出了错误

Incompatible types in assignment (expression has type "int", variable has type "u8")

假设它使用 PEP 563,即。 from __future__ import annotations.

像这样转换它会工作,但它会污染,产生不必要的开销,并且需要在现有代码中工作只更改类型提示,而不是代码:

i: u8 = u8(10)

因此,唯一需要的更改是添加类型提示,而不是更改其余代码。而且不用打字也能正常工作。如果我删除库(假设 PEP 563),代码应该可以正常运行,即使在这种情况下它会在 Mypy:

上出错
from __future__ import annotations

i: u8 = 10  # Works OK without the u8 definition

i: u8 = u8(10)  # ERROR: u8 is not defined here.

我也尝试过将 abc.ABC 与寄存器一起使用,但它 doesn't works:

class u8(int, ABC):
    ...

u8.register(int)

这似乎是一项简单的任务,我一定在这里遗漏了一些非常明显的东西,但到目前为止所有的谷歌搜索都没有帮助。

目前我发现的解决方法是在 TYPE_CHECKING 为真时将 class 定义为 int。上面的例子似乎让 mypy 高兴并给出了我在运行时需要的东西:

from typing import TYPE_CHECKING

class _u8(int):
    _size_bits = 8
    _struct_format: str = 'B'

    def validate(self):
        return 0 <= int(self) <= 255

if TYPE_CHECKING:
    u8 = int
else:
    u8 = _u8

i: u8 = 10

您正试图将非类型信息填充到您的注释中,以便于您正在构建的工具。您已尝试通过创建子类型来做到这一点,但这并不意味着您想要它的意思,并且 mypy 正确地引发了错误。

Python 3.9 中将提供您想要的功能,typing_extensions. It's the Annotated annotation, proposed in PEP 593 中的大多数先前版本(3.5.3+ 和可能是 2.7)都可以使用向后移植。使用 Annotated,您可以定义

u8 = Annotated[int, whatever_arbitrary_data]

并注释

i: u8 = 10

而mypy会识别int是类型,whatever_arbitrary_data是别人的问题。