调用校验助手函数后Mypy仍不识别变量非空类型的问题
我在一个类中定义了实例变量darwin_handler,类型标注为Optional[DarwinHandler](允许为None或DarwinHandler实例)。在get_list_from_darwin函数内,我先调用助手函数_handler_checker——这个函数会检查传入的handler是否为None,如果存在未初始化的handler就抛出异常。但Mypy并没有识别这个校验逻辑,后续我将darwin_handler当作DarwinHandler实例调用方法时,仍然报错提示该变量可能为None。
代码示例
def get_list_from_darwin(self, url: str, view: Dict[str, str], original_time) -> List[str]: self._handler_checker([self.mongo_handler, self.darwin_handler]) change_list = self.darwin_handler.send_request_to_darwin(url, view) ... def _handler_checker(self, handlers_to_check: List[Union[MongoHandler, DarwinHandler, ArangoHandler]]) -> None: handlers_missing = [] for handler in handlers_to_check: if not handler: handlers_missing.append(handler.__class__.__name__) if handlers_missing: raise HandlerNotDefinedError('Following handlers not defined ' f'for {self.name} flow: {handlers_missing}')
Mypy错误信息
在change_list = self.darwin_handler.send_request_to_darwin(url, view)这一行,Mypy报错:
Item "None" of "DarwinHandler | None" has no attribute "send_request_to_darwin" Mypy union-attr
其中self.darwin_handler的类型标注为:darwin_handler: Optional[DarwinHandler] = None
原因
Mypy的静态类型推导无法跨函数追踪实例变量的状态变化。虽然_handler_checker在运行时会确保darwin_handler不为None,但Mypy无法理解该函数的内部逻辑对变量的约束,因此仍然认为darwin_handler可能是None。
解决方法
方法1:使用类型断言或assert语句
直接向Mypy明确变量的类型:
- 使用
cast(仅为类型提示,运行时无检查):
from typing import cast change_list = cast(DarwinHandler, self.darwin_handler).send_request_to_darwin(url, view)
- 使用
assert(运行时会校验,不满足则抛出异常):
assert self.darwin_handler is not None, "darwin_handler must be initialized before use" change_list = self.darwin_handler.send_request_to_darwin(url, view)
方法2:重构助手函数,返回已校验的handler
修改_handler_checker为单个handler校验并返回非None实例,让Mypy能追踪类型:
def _check_single_handler(self, handler: Optional[Union[MongoHandler, DarwinHandler, ArangoHandler]]) -> Union[MongoHandler, DarwinHandler, ArangoHandler]: if not handler: raise HandlerNotDefinedError(f'{handler.__class__.__name__} not defined for {self.name} flow') return handler # 调用方式 self._check_single_handler(self.mongo_handler) valid_darwin_handler = self._check_single_handler(self.darwin_handler) change_list = valid_darwin_handler.send_request_to_darwin(url, view)
这种方式无需额外断言,更符合Mypy的推导逻辑。
方法3:自定义类型守卫函数
定义类型守卫,让Mypy识别校验逻辑:
from typing import TypeGuard def is_valid_handler(handler: Optional[Union[MongoHandler, DarwinHandler, ArangoHandler]]) -> TypeGuard[Union[MongoHandler, DarwinHandler, ArangoHandler]]: return handler is not None # 使用时 assert is_valid_handler(self.darwin_handler) change_list = self.darwin_handler.send_request_to_darwin(url, view)
如果需要批量校验,可结合断言或批量处理逻辑。
内容的提问来源于stack exchange,提问作者yahooo

