Using mypy with with lazy initialization of instance attributes

Question:

UPDATE: trying to check/fill values in another function


I’m trying to use mypy in my projects, but many of the instance attributes I use are only initialized after __init__, and not inside it. However, I do want to keep the good practice of declaring all instance attributes at __init__, so I need some complicated solutions to make this work.

An example to how I want this to behave (currently mypy is complaining):

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

Currently mypy complains about the assignment of self.y, and wants it to be Optional or None.

If I agree with it and change the line to self.y: Optional[int] = None, then mypy complains on the return value of do, because self.y might be None.

The only way I found around it is to add as assert before using self.y, like: assert self.y is not None, which mypy picks up and understands. However, starting each method with many asserts is quite hard. I have many such values, and usually one method that initializes all of them, and all other methods runs after it.

I understand that mypy is rightfully complaining (the method do can be called before fill_values), but even when I try to prevent it I can’t get mypy to accept this. I can extend this example by adding more functionality but mypy can’t infer this:

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

Any idea of a more elegant solution that multiple assert statements and Optional types which obscure the code?

Asked By: Beka

||

Answers:

I have found this to work for me:

class Foo:
def __init__(self, x: int):
    self.x = x
    self.y: int # Give self.y a type but no value

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

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

Essentially all you are doing is telling mypy that self.y will be an integer when (and if) it is initialised. Trying to call self.y before it is initialised will raise an error and you can check if it has been initialised using hasattr(self, "y").

Answered By: Rory Sullivan
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.