Python 将mypy与实例属性的延迟初始化一起使用
更新:尝试检查/填充另一个函数中的值Python 将mypy与实例属性的延迟初始化一起使用,python,mypy,python-typing,Python,Mypy,Python Typing,更新:尝试检查/填充另一个函数中的值 我试图在我的项目中使用mypy,但我使用的许多实例属性仅在\uuuu init\uuuu之后初始化,而不是在它内部初始化。但是,我确实希望保持在\uuuu init\uuuu声明所有实例属性的良好做法,因此我需要一些复杂的解决方案来实现这一点 我希望这一行为的示例(目前mypy正在抱怨): 目前,mypy抱怨分配了self.y,希望它是可选的或无 如果我同意并将行更改为self.y:Optional[int]=None,那么mypy会抱怨do的返回值,因为
我试图在我的项目中使用mypy,但我使用的许多实例属性仅在
\uuuu init\uuuu
之后初始化,而不是在它内部初始化。但是,我确实希望保持在\uuuu init\uuuu
声明所有实例属性的良好做法,因此我需要一些复杂的解决方案来实现这一点
我希望这一行为的示例(目前mypy正在抱怨):
目前,mypy抱怨分配了self.y
,希望它是可选的
或无
如果我同意并将行更改为self.y:Optional[int]=None
,那么mypy会抱怨do
的返回值,因为self.y
可能是None
我找到的唯一解决方法是在使用self.y
之前添加as assert,比如:assert self.y不是None
,mypy会选择并理解它。然而,用许多断言来启动每个方法是相当困难的。我有很多这样的值,通常一个方法初始化所有这些值,所有其他方法都在后面运行
我知道mypy有理由抱怨(方法do
可以在fill\u 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
有没有想过一个更优雅的解决方案,即多个断言语句和模糊代码的
可选类型?请记住,在Foo.fill\u值之前调用Foo.do
是完全可能的,我更喜欢使用可选[int]
类型,然后,如果self.y
未初始化为整数,则从do
引发异常,例如
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”)
检查它是否已初始化。如果self.y
没有合理的默认值,如果在填充值之前调用do
,您会怎么做?您可能希望通过引发异常将此决定传递回调用位置?我已经更新了问题以反映该选项,但mypy仍在抱怨您可以通过尾随的#键入:ignore[assignment]
注释忽略错误的赋值,或者,您可以通过传递完全关闭此行为,但在所描述的@dspencer这样的情况下,您将失去none检查。至于选项的验证:mypy
只理解直接断言,当你把它们放在一个单独的方法中时就不理解了,因为推理变得太麻烦了。如果我有很多变量,这就变得麻烦了。此外,您的建议并没有使mypy沉默,因为它仍然认为self.y
可能是空的,即使在调用self.fill\u values()
之后也是如此。我更新了问题以反映这一点。你是对的,我现在删除了这个选项,因为mypy的静态分析在那时不知道self.y
是否已经初始化。太棒了!它还解决了“通信”的问题,即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