Создать новый подтип int mypy, совместимый с 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 с регистром, но он не работает:

class u8(int, ABC):
    ...

u8.register(int)

Это кажется простой задачей, и мне, должно быть, здесь не хватает чего-то довольно очевидного, но все поиски в Google до сих пор не помогли.


person Allan Deamon    schedule 12.07.2020    source источник
comment
Непонятно, какого поведения вы ожидаете. 10 не является объектом типа u8. У него не будет этого validate метода и он не будет вести себя как объект u8 каким-либо значимым образом.   -  person user2357112 supports Monica    schedule 12.07.2020
comment
Просто начни с myvar = u8(10)   -  person Jan    schedule 12.07.2020
comment
Я добавляю некоторые пояснения, почему я не могу пойти с u8(10). Кроме того, подсказка типа должна использоваться созданным мной инструментом, поэтому мне нужно явно ввести подсказку.   -  person Allan Deamon    schedule 12.07.2020
comment
Какой результат вы ожидаете от ˋi: u8 = 256ˋ или ˋi: u8 = -1ˋ?   -  person MisterMiyagi    schedule 12.07.2020
comment
Во время выполнения, если инструмент используется и проверка включена, генерировать исключение в некоторой части кода, когда инструмент должен действовать (в данном случае для упаковки (сериализации) класса). Это еще не ясно, потому что я его создаю, но намерение состоит в том, чтобы при необходимости выполнять проверки во время выполнения.   -  person Allan Deamon    schedule 12.07.2020
comment
Я имею ввиду, какого результата вы ждете от MyPy?   -  person MisterMiyagi    schedule 12.07.2020
comment
@MisterMiyagi ничего особенного: просто поймите, что это целое число без нареканий. Никакой дополнительной проверки. Проверка будет использоваться инструментом во время выполнения. Возможно, в будущем я мог бы написать плагин mypy, который использует это, но при статическом анализе выгода будет очень небольшой или почти нулевой. Все, что полезно статически, было бы слишком сложным, чтобы его стоило писать.   -  person Allan Deamon    schedule 13.07.2020


Ответы (2)


Вы пытаетесь вставить в свои аннотации информацию, не относящуюся к типу, в пользу инструмента, который вы создаете. Вы пытались сделать это, создав подтип, но это не означает то, что вы хотите, и mypy правильно выдает ошибку.

Функция для того, что вы хотите, появится в Python 3.9, с резервным портом, доступным для большинства предыдущих версий (3.5.3+ и, возможно, 2.7) в _ 1_. Это Annotated аннотация, предложенная в PEP 593. С Annotated вы можете определить

u8 = Annotated[int, whatever_arbitrary_data]

и комментируйте такие вещи, как

i: u8 = 10

и mypy распознает, что int - это тип, а whatever_arbitrary_data - это чья-то проблема.

person user2357112 supports Monica    schedule 12.07.2020
comment
На самом деле я использую python 3.9, поэтому я попробую это. Annotated[type, extra] уродлив, но я забыл, что могу использовать его таким образом, и это, вероятно, будет идеальным решением. - person Allan Deamon; 12.07.2020
comment
Действительно, в документе PEP 593 в качестве примера цитируется гипотетическая библиотека, которую я делаю именно так. - person Allan Deamon; 12.07.2020

Обходной путь, который я нашел до сих пор, - определить класс как int, когда TYPE_CHECKING истинно. Приведенный выше пример, кажется, радует 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
person Allan Deamon    schedule 12.07.2020
comment
Но затем всякий раз, когда вы пытаетесь использовать u8 для чего-либо, mypy будет жаловаться, что int не имеет методов и атрибутов, которые вы пытаетесь использовать. - person user2357112 supports Monica; 12.07.2020
comment
И если вы никогда не пытаетесь использовать u8 для чего-либо, тогда вообще нет смысла создавать его. - person user2357112 supports Monica; 12.07.2020
comment
Метод проверки будет использоваться внутри инструмента, который создает тип u8, поэтому я могу отключить проверку типов для каждой строки. Но указание типа инструмента не должно создавать ошибку mypy для пользователя, использующего тип u8. - person Allan Deamon; 12.07.2020
comment
Проще говоря: внутри инструмента: я могу использовать u8.validate, а при необходимости могу использовать # typing: ignore. в коде с использованием инструмента: просто добавляет подсказку типа и импортирует инструмент, но не меняет остальную часть кода и не создает ошибку mypy. - person Allan Deamon; 12.07.2020