如何避免python中的并行类层次结构

2024-10-06 15:16:23 发布

您现在位置:Python中文网/ 问答频道 /正文

我最近在我的python代码中遇到了一种奇怪的气味,我认为这与并行继承有关。下面是我编造的一个小例子:

class DogHabits:
    def __init__(self):
        self.habits = ['lick butt']

class GermanShepherdHabits(DogHabits):
    def __init__(self):
        super().__init__()
        self.habits.extend(['herd sheep'])

class LabradorHabits(DogHabits):
    def __init__(self):
        super().__init__()
        self.habits.extend(['hunt', 'pee on owner'])

class Dog:
    def __init__(self):
        self.type = 'generic_dog'
        self.my_habits = DogHabits()

    def do_stuff(self):
        for habit in self.my_habits.habits:
            print(habit)

class GermanShepherd(Dog):
    def __init__(self):
        self.type = 'german shepherd'
        self.my_habits = GermanShepherdHabits()

class Labrador(Dog):
    def __init__(self):
        self.type = 'labrador'
        self.my_habits = LabradorHabits()

if __name__ == "__main__":
    german_shepherd = GermanShepherd()
    print('\n{}'.format(german_shepherd.type))
    german_shepherd.do_stuff()


    labrador = Labrador()
    print('\n{}'.format(labrador.type))
    labrador.do_stuff()

我有一个通用的dog类,具体的dog实现从中继承。每个狗类(包括泛型/抽象类)都有一组习惯,其本身由另一个习惯类层次结构表示

我对两个层次结构必须始终完全相同这一事实感到恼火。此外,DogHabits之间的继承在习惯层次结构中很有用,但在dogs层次结构中却没有用,因为我需要为dog层次结构中的每个类实例化一个单独的habits对象

什么是解药?我可能想添加许多dog类的实现,更新相应的习惯层次结构听起来很乏味,而且很难闻


Tags: self层次结构initmydeftypeclass习惯
3条回答

如果习惯需要类属性,而不是实例属性,那么这实际上可能是元类的一个很好的用途

习惯不一定是一个简单的列表,它可以是其他的东西,只要有一个概念,除了以前的和返回新的。(__add____radd__在一个习惯类上,我认为会达到这个效果)

class DogType(type):

    def __init__(cls, name, bases, attrs):
        """ this is called at the Dog-class creation time.  """

        if not bases:
            return

        #pick the habits of direct ancestor and extend it with 
        #this class then assign to cls.
        if "habits" in attrs:
            base_habits = getattr(bases[0], "habits", [])
            cls.habits = base_habits + cls.habits


class Dog(metaclass=DogType):
    habits = ["licks butt"]

    def __repr__(self):
        return f"My name is {self.name}.  I am a {self.__class__.__name__} %s and I like to {self.habits}"

    def __init__(self, name):
        """ dog instance can have all sorts of instance variables"""
        self.name = name

class Sheperd(Dog):
    habits = ["herds sheep"]

class GermanSheperd(Sheperd):
    habits = ["bites people"]

class Poodle(Dog):
    habits = ["barks stupidly"]

class StBernard(Dog):
    pass


for ix, cls in enumerate([GermanSheperd, Poodle, StBernard]):
    name = f"dog{ix}"
    dog = cls(name)
    print(dog)

输出:

My name is dog0.  I am a GermanSheperd %s and I like to ['licks butt', 'herds sheep', 'bites people']
My name is dog1.  I am a Poodle %s and I like to ['licks butt', 'barks stupidly']
My name is dog2.  I am a StBernard %s and I like to ['licks butt']

这个答案假设DogHabits比一个简单的列表要复杂得多,并且确实值得一个拥有自己继承的专用类

从设计的角度来看,我可以看到关于habitstype应该是类成员还是实例成员的第一个问题。在这里,这个答案再次假设有理由让他们成为实例成员

我将使Habits成为Dogs的内部类,并在类文档中声明,可以通过在Dogs的子类中构建它的子类来定制:

 class Dog:
    class Habits:
        """Represents the habits of a Dog.

        It can be customized in a child class by creating in the subclass an
        inner class named Habits that would be a subclass of Dog.Habits
        """

        def __init__(self):
            self.habits = ['lick butt']

    def __init__(self, typ='generic_dog'):
        self.type = typ
        self.my_habits = self.__class__.Habits()

    def do_stuff(self):
        for habit in self.my_habits.habits:
            print(habit)


class GermanShepherd(Dog):

    class Habits(Dog.Habits):

        def __init__(self):
            super().__init__()
            self.habits.extend(['herd sheep'])

    def __init__(self):
        super().__init__('german shepherd')


class Labrador(Dog):

    class Habits(Dog.Habits):
        def __init__(self):
            super().__init__()
            self.habits.extend(['hunt', 'pee on owner'])

    def __init__(self):
        super().__init__('labrador')

这可能太离谱了,但我认为不需要单独的DogHabitshabits应该是类属性,而不是实例属性,并且可以由__init_subclass__设置

class Dog:
    habits = ['lick butts']

    def __init_subclass__(cls, habits=None, **kwargs):
        super().__init_subclass__(**kwargs)
        if habits is not None:
            cls.habits = cls.habits + habits


class GermanShepherd(Dog, habits=['herd sheep']):
    def __init__(self):
        self.type = 'german shepherd'


class Labrador(Dog, habits=['pee on owner']):
    def __init__(self):
        self.type = 'labrador'

type本身更像是类属性而不是实例属性,因为它只是类本身已经编码的信息的(替代)字符串表示。由于您不会附加到现有值,因此在必要时设置class属性比通过__init_subclass更容易:

class Dog:
    habits = ['lick butts']
    type = 'generic_dog'

    def __init_subclass__(cls, habits=None, **kwargs):
        super().__init_subclass__(**kwargs)
        if habits is not None:
            cls.habits = cls.habits + habits


class GermanShepherd(Dog, habits=['herd sheep']):
    type = 'german shepard'


class Labrador(Dog, habits=['pee on owner']):
    type = 'labrador'


class BlackLabrador(Labrador):
    pass  # E.g. if you are happy with inheriting Labrador.type

相关问题 更多 >