使用Python抽象类实现单一职责原则及DB模型关联报错排查
解决Pyright提示
cls无db属性的问题 你的核心需求是DB模型和业务类分离,同时复用get()、get_all()的逻辑,Pyright报错是因为它不知道Animal的子类都会定义db类属性。下面是具体的修复方案:
步骤1:统一DB模型的抽象接口
先给所有DB模型定义一个抽象基类,让DogsDB、CatsDB继承它,这样能让类型检查工具明确db的结构:
from abc import ABC, abstractmethod from typing import ClassVar, Type, List
class AnimalDB(ABC): lists: ClassVar[List[str]] @classmethod @abstractmethod def get(cls, id: int) -> str: pass
然后修改DogsDB和CatsDB继承这个抽象类:
class DogsDB(AnimalDB): lists = ["DOG1", "DOG2", "DOG3"] @classmethod def get(cls, id: int) -> str: return cls.lists[id] class CatsDB(AnimalDB): lists = ["CAT1", "CAT2", "CAT3"] @classmethod def get(cls, id: int) -> str: return cls.lists[id]
步骤2:给Animal类添加类型注解
在Animal类中明确标注db是类变量,并且类型是AnimalDB的子类,这样Pyright就能识别子类会实现这个属性:
class Animal(ABC): # 明确标注db是类变量,类型为AnimalDB的子类 db: ClassVar[Type[AnimalDB]] def __init__(self, name: str): self.name = name # 替换abstractstaticmethod为标准写法 @staticmethod @abstractmethod def save(m) -> None: pass @staticmethod @abstractmethod def _from_model(obj: str) -> 'Animal': pass @classmethod def get(cls, id: int) -> 'Animal': obj = cls.db.get(id) return cls._from_model(obj) @classmethod def get_all(cls) -> List['Animal']: objs = cls.db.lists return [cls._from_model(obj) for obj in objs] def __repr__(self) -> str: return self.name
步骤3:保持原有业务类不变
Dog和Cat类的代码不需要改动,依然保持db = DogsDB/db = CatsDB的赋值即可。
修复后的完整代码
from abc import ABC, abstractmethod from typing import ClassVar, Type, List class AnimalDB(ABC): lists: ClassVar[List[str]] @classmethod @abstractmethod def get(cls, id: int) -> str: pass class DogsDB(AnimalDB): lists = ["DOG1", "DOG2", "DOG3"] @classmethod def get(cls, id: int) -> str: return cls.lists[id] class CatsDB(AnimalDB): lists = ["CAT1", "CAT2", "CAT3"] @classmethod def get(cls, id: int) -> str: return cls.lists[id] class Animal(ABC): db: ClassVar[Type[AnimalDB]] def __init__(self, name: str): self.name = name @staticmethod @abstractmethod def save(m) -> None: pass @staticmethod @abstractmethod def _from_model(obj: str) -> 'Animal': pass @classmethod def get(cls, id: int) -> 'Animal': obj = cls.db.get(id) return cls._from_model(obj) @classmethod def get_all(cls) -> List['Animal']: objs = cls.db.lists return [cls._from_model(obj) for obj in objs] def __repr__(self) -> str: return self.name class DogSound: def __init__(self, name: str): self.name = name def sound(self) -> None: print(self.name, ": DOG SOUND!!") class Dog(Animal, DogSound): db = DogsDB def __init__(self, name: str, age: int): super(Dog, self).__init__(name) self.age = age @staticmethod def save(m) -> None: print(m) @staticmethod def _from_model(obj: str) -> 'Dog': return Dog(obj, 4) class Cat(Animal): db = CatsDB def __init__(self, name: str, age: int): super().__init__(name) self.age = age @staticmethod def save(m) -> None: print(m) @staticmethod def _from_model(obj: str) -> 'Cat': return Cat(obj, 4) print(Cat.get(1)) print(Dog.get(1)) print(Cat.get_all()) print(Dog.get_all()) Dog.get(1).sound()
关键说明
- 类型注解的作用:通过
ClassVar[Type[AnimalDB]]告诉Pyright,Animal的所有子类都会有一个名为db的类属性,且这个属性是AnimalDB的子类,消除了类型检查错误。 - 统一DB抽象类:让所有DB模型继承
AnimalDB,既规范了DB层的接口,也让类型检查工具能识别db的方法和属性。 - 简化
get_all逻辑:用列表推导式替代原有的循环,代码更简洁高效。
内容的提问来源于stack exchange,提问作者ohxdMAGsDCiCJ
相关产品推荐
相关产品推荐

