Использование mypy с ленивой инициализацией атрибутов экземпляра - PullRequest
1 голос
/ 30 марта 2020

ОБНОВЛЕНИЕ : пытаюсь проверить / заполнить значения в другой функции


Я пытаюсь использовать mypy в своих проектах, но многие из атрибутов экземпляра, которые я использую, только инициализируется после __init__, а не внутри него. Тем не менее, я хочу сохранить хорошую практику объявления всех атрибутов экземпляра на __init__, поэтому мне нужны некоторые сложные решения для этой работы.

Пример того, как я хочу, чтобы это происходило (сейчас mypy жалоба):

from typing import Optional

class Foo:
    def __init__(self, x: int):
        self.x = x
        self.y: int = None  # will initialize later, but I know it will be an int

    def fill_values(self):
        self.y = x**2

    def do(self) -> int:
        return self.x + self.y

В настоящее время mypy жалуется на присвоение self.y и хочет, чтобы оно было Optional или None.

Если я согласен с этим и изменю до self.y: Optional[int] = None, тогда mypy жалуется на возвращаемое значение do, потому что self.y может быть None.

Единственный способ, который я нашел, это добавить assert перед использованием self.y, например: assert self.y is not None, который mypy подхватывает и понимает. Однако начинать каждый метод со многими утверждениями довольно сложно. У меня много таких значений, и обычно один метод, который инициализирует все из них, и все другие методы выполняются после него.

Я понимаю, что mypy справедливо жалуется (метод do можно вызвать до fill_values ), но даже когда я пытаюсь предотвратить это, я не могу заставить себя принять это. Я могу расширить этот пример, добавив больше функциональности, но mypy не может вывести это:

from typing import Optional

class Foo:
    def __init__(self, x: int):
        self.x = x
        self.y: int = None  # will initialize later, but I know it will be an int

    def fill_values(self):
        self.y = x**2

    def check_values(self):
        assert self.y is not None

    def do(self) -> int:
        if self.y is None:
            self.fill_values()
        self.check_values()
        return self.x + self.y

Есть идея более элегантного решения, которое состоит из нескольких операторов assert и Optional типов, которые затемняют код?

1 Ответ

1 голос
/ 30 марта 2020

Имея в виду, что вполне возможно, что Foo.do будет вызываться до Foo.fill_values, я предпочитаю использовать тип Optional[int], а затем повысить исключение из do, если self.y не было инициализировано в целое число, например

from typing import Optional

class UninitializedAttributeError(Exception):
    pass

class Foo:
    def __init__(self, x: int):
        self.x = x
        self.y: Optional[int] = None

    def fill_values(self):
        self.y = x**2

    def do(self) -> int:
        if self.y is None:
            raise UninitializedAttributeError("Foo.y is uninitialized")
        return self.x + self.y
...