我有一个用数据填充的可变对象。一旦所有强制数据都存在,就可以“提交”对象。尝试提交不完整的对象会引发异常。这是一个玩具示例,其中一个对象的content
初始值为None
, 并且必须在提交之前填充一个字符串。
# inline.py
from typing import Optional
class IncompleteFoo(Exception):
pass
class Foo:
def __init__(self) -> None:
self.content = None #type: Optional[str]
def commit(self) -> str:
if self.content is None:
raise IncompleteFoo
return self.content
这段代码的结构不是很好:应该有一个单独的方法来检查完整性。(在我的真实代码中,该方法会在多个地方被调用,因为有几种不同的“提交”方式。)
# check.py:14: error: Incompatible return value type (got "Optional[str]", expected "str")
from typing import Optional
class IncompleteFoo(Exception):
pass
class Foo:
def __init__(self) -> None:
self.content = None #type: Optional[str]
def check_completeness(self) -> None:
if self.content is None:
raise IncompleteFoo
def commit(self) -> str:
self.check_completeness()
return self.content
我使用 mypy 0.780 来检查类型。可以理解的是,它抱怨上面的代码:
check.py:15: error: Incompatible return value type (got "Optional[str]", expected "str")
这很公平:在第一个“内联”版本中,mypy 足够聪明,可以知道它self.content
具有类型str
,因为它具有类型Optional[str]
,并且这部分代码只有在self.content is None
为 false 时才可访问。在具有check_completeness
单独方法的版本中,mypy 不会推断该方法的后置条件是 that self.content
is not None
。
如何让 mypy 知道check_completeness
is self.content is not None
or的后置条件self.content : str
?为了保留完整性检查的封装(在我的真实代码中要大得多),我不想重复里面的条件commit
。我宁愿保持commit
上述第二个版本不变。我可以满足于重复:
# assert.py
from typing import Optional
class IncompleteFoo(Exception):
pass
class Foo:
def __init__(self) -> None:
self.content = None #type: Optional[str]
def check_completeness(self) -> None:
if self.content is None:
raise IncompleteFoo
def is_complete(self) -> bool:
return self.content is not None
def commit(self) -> str:
self.check_completeness()
assert self.is_complete()
return self.content
但这无济于事:mypy 不会扩展方法调用来推断调用的后置条件assert
。