Укажите тип переменных экземпляра, используя комментарии типа python 2

Я пытаюсь указать тип переменной экземпляра, используя PEP 484 синтаксис Python 2. Однако я не нашел способа добавить типы без инициализации переменной в python 2, что эквивалентно следующему python 3:

value: int

Моя обычная работа для этого состоит в том, чтобы объявить тип переменной в __init__ при создании экземпляра переменной. Однако это не работает для протоколов, где тип переменной экземпляра должен быть частью протокола (типы в __init__, похоже, не учитываются). Вот пример в Python 3, где я использую реализацию по умолчанию:

from typing_extensions import Protocol
class A(Protocol):
    value: int

    def get_value(self) -> int:
        return self.value

Затем это выделит ошибки, если value не инициализирован должным образом:

class B(A):
    pass
B()  # error: Cannot instantiate abstract class 'B' with abstract attribute 'value'

Однако преобразование этого в комментарии типа python 2 не может передать mypy. Это дает ту же ошибку с объявлением __init__ или без него.

class A(Protocol):
    def __init__(self):
        # type: () -> None
        self.value = 0  # type: int
    def get_value(self):
        # type: () -> int
        return self.value  # error: "A" has no attribute "value"

Есть ли какой-то специальный синтаксис для объявления типов переменных без их инициализации в python 2?


person Quantum7    schedule 11.09.2019    source источник
comment
Я думаю, именно поэтому Протокол находится в typing_extensions в 2.7, а не typing.   -  person Quantum7    schedule 11.09.2019
comment
@BoarGules Это крайне бесполезный комментарий, поскольку подразумевает, что это явное решение, принятое в mypy. Это больше похоже на баг.   -  person Markus Unterwaditzer    schedule 11.09.2019


Ответы (1)


Протоколы Mypy используют переменные класса для определения атрибутов. В противном случае mypy не делает особенно тонкого различия между переменными класса и экземпляра. Собрав эти две вещи вместе, вы можете написать следующий код:

from typing_extensions import Protocol

class A(Protocol):
    value = None  # type: int

    def get_value(self):
        # type: () -> int
        return self.value

# below here it's just to validate that the protocol works

class B(object):
    def __init__(self, value):
        # type: (int) -> None
        self.value = value

    def get_value(self):
        # type: () -> int
        return self.value


a = B(42)  # type: A
person Markus Unterwaditzer    schedule 11.09.2019
comment
Это работает, к моему удивлению! A.value — это переменная класса, а не переменная экземпляра; однако mypy, похоже, также принимает объекты с переменными экземпляра как реализующие A. Изменение B, чтобы иметь def __init__(self): self.value = 42 (переменная экземпляра), отлично проверяет тип, что было именно то, что я искал. - person Quantum7; 17.09.2019
comment
Если вы обновите свой ответ, чтобы использовать переменную экземпляра в B, я приму это. - person Quantum7; 17.09.2019
comment
Сделанный! Я думаю, что в процессе обновления этого ответа я начал лучше понимать вопрос. - person Markus Unterwaditzer; 17.09.2019