How To Avoid Parallel Class Hierarchy In Python
Solution 1:
This might be going too far afield, but I don't see the need for a separate DogHabits
class. habits
should be a class attribute, not an instance attribute, and could be set by __init_subclass__
.
classDog:
habits = ['lick butts']
def__init_subclass__(cls, habits=None, **kwargs):
super().__init_subclass__(**kwargs)
if habits isnotNone:
cls.habits = cls.habits + habits
classGermanShepherd(Dog, habits=['herd sheep']):
def__init__(self):
self.type = 'german shepherd'classLabrador(Dog, habits=['pee on owner']):
def__init__(self):
self.type = 'labrador'
type
itself is also more of a class attribute than an instance attribute, as it's simply an (alternate) string representation of information already encoded by the class itself. Since you wouldn't append to an existing value, it's easier to just set the class attribute where necessary rather than going through __init_subclass
:
classDog:
habits = ['lick butts']
type = 'generic_dog'def__init_subclass__(cls, habits=None, **kwargs):
super().__init_subclass__(**kwargs)
if habits isnotNone:
cls.habits = cls.habits + habits
classGermanShepherd(Dog, habits=['herd sheep']):
type = 'german shepard'classLabrador(Dog, habits=['pee on owner']):
type = 'labrador'classBlackLabrador(Labrador):
pass# E.g. if you are happy with inheriting Labrador.type
Solution 2:
IF habits need to a class attribute, rather than instance attributes, this may actually be a good use for metaclasses.
Habits need not be a simple list, it could be something else, as long as there is the notion of addition to previous and return new. (__add__
or __radd__
on a Habits class would do the trick I think)
classDogType(type):
def__init__(cls, name, bases, attrs):
""" this is called at the Dog-class creation time. """ifnot 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
classDog(metaclass=DogType):
habits = ["licks butt"]
def__repr__(self):
returnf"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
classSheperd(Dog):
habits = ["herds sheep"]
classGermanSheperd(Sheperd):
habits = ["bites people"]
classPoodle(Dog):
habits = ["barks stupidly"]
classStBernard(Dog):
passfor ix, cls inenumerate([GermanSheperd, Poodle, StBernard]):
name = f"dog{ix}"
dog = cls(name)
print(dog)
output:
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']
Solution 3:
This answer assumes that the DogHabits is much more complex than a mere list and is really worth a dedicated class with its own inheritance.
On a design point of view, I can see a first question on whether habits
and type
should be class or instance members. Here again, this answer assumes that there are reasons to make them instance members.
I would make Habits
an inner class of Dogs
and state in the class documentation that is can be customized by building a subclass of it in a subclass of Dogs
:
classDog:
classHabits:
"""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()
defdo_stuff(self):
for habit in self.my_habits.habits:
print(habit)
classGermanShepherd(Dog):
classHabits(Dog.Habits):
def__init__(self):
super().__init__()
self.habits.extend(['herd sheep'])
def__init__(self):
super().__init__('german shepherd')
classLabrador(Dog):
classHabits(Dog.Habits):
def__init__(self):
super().__init__()
self.habits.extend(['hunt', 'pee on owner'])
def__init__(self):
super().__init__('labrador')
Post a Comment for "How To Avoid Parallel Class Hierarchy In Python"