You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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()

关键说明

  1. 类型注解的作用:通过ClassVar[Type[AnimalDB]]告诉Pyright,Animal的所有子类都会有一个名为db的类属性,且这个属性是AnimalDB的子类,消除了类型检查错误。
  2. 统一DB抽象类:让所有DB模型继承AnimalDB,既规范了DB层的接口,也让类型检查工具能识别db的方法和属性。
  3. 简化get_all逻辑:用列表推导式替代原有的循环,代码更简洁高效。

内容的提问来源于stack exchange,提问作者ohxdMAGsDCiCJ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 05:35:42