mypy结合overload与contextmanager实现文件打开器抽象类的类型报错如何解决
问题原因
- 重载签名上误加了
@contextmanager装饰器:该装饰器仅需要作用于最终的方法实现,不需要标注在重载的桩函数上。 - 子类方法返回值类型与父类不匹配:
@contextmanager装饰器会将返回Iterator[T]的生成器函数包装为返回ContextManager[T]的可调用对象,因此子类的方法签名需要和父类保持一致,使用ContextManager作为返回值类型,而非Iterator。 - 装饰器顺序问题:
@abstractmethod需要放在最靠近方法定义的位置,避免装饰器执行顺序错误导致抽象方法识别异常。
修复后的代码
仅需要修改子类FileOpener的定义即可,完整可通过检查的代码如下:
from abc import ABC, abstractmethod from contextlib import contextmanager from typing import ContextManager, Iterator, Literal, Union, overload class Reader: """A file reader.""" def __init__(self, path: str) -> None: self.path = path class Writer: """A file writer.""" def __init__(self, path: str) -> None: self.path = path class FileOpenerAbstract(ABC): @overload @abstractmethod def open(self, path: str, mode: Literal['r'] ) -> ContextManager[Reader]: ... @overload @abstractmethod def open(self, path: str, mode: Literal['x', 'w', 'a'] ) -> ContextManager[Writer]: ... @abstractmethod def open( self, path: str, mode: Literal['r', 'x', 'w', 'a'] = 'r' ) -> Union[ContextManager[Reader], ContextManager[Writer]]: """Open an archive""" class FileOpener(FileOpenerAbstract): @overload def open(self, path: str, mode: Literal['r'] ) -> ContextManager[Reader]: ... @overload def open(self, path: str, mode: Literal['x', 'w', 'a'] ) -> ContextManager[Writer]: ... @contextmanager def open( self, path: str, mode: Literal['r', 'x', 'w', 'a'] = 'r', ) -> Union[ContextManager[Reader], ContextManager[Writer]]: if mode == 'r': yield Reader(path) else: yield Writer(path)
以上代码在Python 3.8 + mypy 0.910环境下可正常通过类型检查,同时Pylance、pylint均可正常解析。
内容的提问来源于stack exchange,提问作者Chris Sewell
相关产品推荐
相关产品推荐

