如何为MicroPython类装饰器添加类型注解以通过mypy检查?
解决MicroPython可序列化类的mypy类型检查问题
问题根源
现有代码的类型问题主要集中在这几点:
- 错误将元类
ABCMeta作为实例类型使用,导致类型注解逻辑混乱 - 装饰器缺少泛型类型注解,mypy无法推断返回类的具体类型
- 动态添加的实例属性(如
Test.a、Test.b)无法被mypy识别 deserialize方法的返回类型标注过于宽泛,不符合实际返回的具体类类型
修正方案
以下是调整后的代码,完全适配mypy严格类型检查:
from abc import ABC, abstractmethod from typing import TypeVar, Type, Any, List, Tuple # 定义泛型类型变量,绑定被装饰的具体类 T = TypeVar('T', bound='SerializableBase') class SerializableBase(ABC): # 定义实例层的抽象方法,用ABC作为基类而非元类 @abstractmethod def serialize(self) -> str: pass @staticmethod @abstractmethod def deserialize(data: str) -> 'SerializableBase': pass def serializable(cls: Type[T]) -> Type[T]: # 校验fields存在并添加类型约束 if not hasattr(cls, 'fields'): raise ValueError('fields is required') fields: List[Tuple[str, type]] = cls.fields # 重写__init__并添加类型注解,同时给实例注入属性注解 def __init__(self: T, **kwargs: Any) -> None: for fname, _ in fields: if fname not in kwargs: raise ValueError(f'{fname} is required') setattr(self, fname, kwargs.get(fname)) # 给实例添加属性注解,让mypy识别动态生成的属性 self.__annotations__ = {fname: ftype for fname, ftype in fields} cls.__init__ = __init__ # 修正serialize的self类型为具体泛型类T def serialize(self: T) -> str: return f"{cls.__name__} {' '.join(str(getattr(self, fname)) for fname, _ in fields)}" cls.serialize = serialize # 修正deserialize的返回类型为具体类T @staticmethod def deserialize(data: str) -> T: parts = data.split(' ') if parts[0] != cls.__name__: raise ValueError(f'invalid data not a {cls.__name__}') if len(parts) - 1 != len(fields): raise ValueError('invalid data not enough fields') return cls(**{fname: ftype(parts[i+1]) for i, (fname, ftype) in enumerate(fields)}) cls.deserialize = deserialize # 确保类继承自SerializableBase(若未显式继承) if not issubclass(cls, SerializableBase): cls.__bases__ = (SerializableBase,) + cls.__bases__ return cls
使用示例(适配mypy)
@serializable class Test: # 给fields添加明确的类型标注 fields: List[Tuple[str, type]] = [ ('a', int), ('b', str) ] x = Test(a=1, b='hello') print(x.a) # mypy可识别x.a类型为int print(x.b) # mypy可识别x.b类型为str # 反序列化的类型可被正确推断 y = Test.deserialize('Test 1 world') reveal_type(y) # mypy会输出: Revealed type is "__main__.Test"
关键修改说明
- 重构抽象基类:将原元类改为实例层的抽象基类
SerializableBase,用ABC作为基类,明确区分元类与实例类型的职责。 - 添加泛型支持:通过
TypeVar绑定具体被装饰类,让装饰器能准确推断返回类的类型,包括deserialize的返回值类型。 - 显式类型标注:给
fields、装饰器参数、方法参数添加明确类型注解,让mypy能解析结构逻辑。 - 动态属性识别:在
__init__中给实例注入__annotations__,让mypy能识别动态生成的实例属性。 - 修正方法类型:将
serialize的self类型从宽泛的基类改为具体泛型类T,确保类型推断精准。
内容的提问来源于stack exchange,提问作者sqrtroot
相关产品推荐
相关产品推荐

