将 mypy 与实例属性的惰性初始化一起使用

Using mypy with with lazy initialization of instance attributes

UPDATE:尝试 check/fill 另一个函数中的值


我正在尝试在我的项目中使用 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 的分配,并希望它是 OptionalNone.

如果我同意并将行更改为 self.y: Optional[int] = None,那么 mypy 会抱怨 do 的 return 值,因为 self.y 可能是 None.

我发现的唯一方法是在使用 self.y 之前添加 as assert,例如:assert self.y is not None,mypy 会选择并理解它。然而,用许多断言启动每个方法是相当困难的。我有很多这样的值,通常一个方法初始化所有这些值,所有其他方法都在它之后运行。

我明白 mypy 的抱怨是正确的(方法 do 可以在 fill_values 之前调用),但即使我试图阻止它,我也无法让 mypy 接受它。我可以通过添加更多功能来扩展此示例,但 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

有没有更优雅的解决方案,多个断言语句和 Optional 类型会混淆代码?

请记住,Foo.do 完全有可能在 Foo.fill_values 之前被调用,我更喜欢使用 Optional[int] 类型,然后从 [=14= 引发异常] 如果 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

我发现这对我有用:

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 = x**2

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

基本上你所做的就是告诉 mypy self.y 在(如果)它被初始化时将是一个整数。在初始化之前尝试调用 self.y 会引发错误,您可以使用 hasattr(self, "y").

检查它是否已初始化