简体   繁体   English

用python抽象类实现单一职责原则

[英]Achieving single-responsibility principle with python abstract classes

I want to separate the DB models from the actual classes.我想将数据库模型与实际类分开。 But i need two static functions for fetching data from the DB regardless of the subclass type.但是无论子类类型如何,我都需要两个 static 函数来从数据库中获取数据。 the implementation for both functions are the same across all DB models.这两个功能的实现在所有数据库模型中都是相同的。

pyright showing an error that cls inside get() and get_all() functions doesn't have a db property. pyright显示get()get_all()函数中的cls没有db属性的错误。

from abc import ABC, abstractstaticmethod


class DogsDB:
    lists = ["DOG1", "DOG2", "DOG3"]

    @classmethod
    def get(cls, id):
        return cls.lists[id]


class CatsDB:
    lists = ["CAT1", "CAT2", "CAT3"]

    @classmethod
    def get(cls, id):
        return cls.lists[id]


class Animal(ABC):
    def __init__(self, name):
        self.name = name

    @abstractstaticmethod
    def save(m):
        pass

    @abstractstaticmethod
    def _from_model(obj):
        pass

    @classmethod
    def get(cls, id):
        obj = cls.db.get(id)
        return cls._from_model(obj)

    @classmethod
    def get_all(cls):
        objs = cls.db.lists

        lists = []
        for obj in objs:
            e = cls._from_model(obj)
            lists.append(e)
        return lists

    def __repr__(self):
        return self.name


class DogSound:
    def __init__(self, name):
        self.name = name

    def sound(self):
        print(self.name, ": DOG SOUND!!")


class Dog(Animal, DogSound):
    db = DogsDB

    def __init__(self, name, age):
        super(Dog, self).__init__(name)
        self.age = age

    @staticmethod
    def save(m):
        print(m)

    @staticmethod
    def _from_model(obj):
        return Dog(obj, 4)


class Cat(Animal):
    db = CatsDB

    def __init__(self,  name, age):
        super().__init__(name)
        self.age = age

    @staticmethod
    def save(m):
        print(m)

    @staticmethod
    def _from_model(obj):
        return Cat(obj, 4)


print(Cat.get(1))
print(Dog.get(1))
print(Cat.get_all())
print(Dog.get_all())
Dog.get(1).sound()

I cannot duplicate your first error.我无法复制您的第一个错误。

Your second issue is a result of method sound implicitly returning None since it has no return statement and you have print(Dog.get(1).sound()) , which will print out the return value from that method.您的第二个问题是方法sound隐式返回None的结果,因为它没有 return 语句并且您有print(Dog.get(1).sound()) ,它将打印出该方法的返回值。 You either want to change this to just Dog.get(1).sound() or modify the sound method to return what it is currently being printed and remove the print statement (my choice).您要么想将其更改为仅Dog.get(1).sound() ,要么修改sound方法以返回当前正在打印的内容并删除print语句(我的选择)。

As an aside, I found this class structure a bit difficult to follow.顺便说一句,我发现这个 class 结构有点难以理解。 Why do you need a separate DogSound class with a name attribute which should belong to Animal ?为什么你需要一个单独的DogSound class 和一个应该属于Animalname属性? Also, it seems to me that age could/should be an attribute of Animal since both cats and dogs have an age.另外,在我看来, age可能/应该是Animal的一个属性,因为猫和狗都有年龄。

from abc import ABC, abstractstaticmethod


class DogsDB:
    lists = ["DOG1", "DOG2", "DOG3"]

    @classmethod
    def get(cls, id):
        return cls.lists[id]


class CatsDB:
    lists = ["CAT1", "CAT2", "CAT3"]

    @classmethod
    def get(cls, id):
        return cls.lists[id]


class Animal(ABC):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @abstractstaticmethod
    def save(m):
        pass

    @abstractstaticmethod
    def _from_model(obj):
        pass

    @classmethod
    def get(cls, id):
        obj = cls.db.get(id)
        return cls._from_model(obj)

    @classmethod
    def get_all(cls):
        objs = cls.db.lists

        lists = []
        for obj in objs:
            e = cls._from_model(obj)
            lists.append(e)
        return lists

    def __repr__(self):
        return self.name

class Dog(Animal):
    db = DogsDB

    def __init__(self, name, age):
        super().__init__(name, age)

    def sound(self):
        return f"{self.name}: DOG SOUND!!"

    @staticmethod
    def save(m):
        print(m)

    @staticmethod
    def _from_model(obj):
        return Dog(obj, 4)


class Cat(Animal):
    db = CatsDB

    def __init__(self,  name, age):
        super().__init__(name, age)
        self.age = age

    @staticmethod
    def save(m):
        print(m)

    @staticmethod
    def _from_model(obj):
        return Cat(obj, 4)


print(Cat.get(1))
print(Dog.get(1))
print(Cat.get_all())
print(Dog.get_all())
print(Dog.get(1).sound())

Prints:印刷:

CAT2
DOG2
[CAT1, CAT2, CAT3]
[DOG1, DOG2, DOG3]
DOG2: DOG SOUND!!

If for some reason you want DogSound to be a separate class, then there is no need for the name attribute to be duplicated:如果出于某种原因您希望DogSound成为一个单独的 class,则无需复制name属性:

...
class DogSound: # A "Mixin" class
    def sound(self):
        return f"{self.name}: DOG SOUND!!"


class Dog(Animal, DogSound):
    db = DogsDB

    def __init__(self, name, age):
        super().__init__(name, age)

    @staticmethod
    def save(m):
        print(m)

    @staticmethod
    def _from_model(obj):
        return Dog(obj, 4)
...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM