I'm facing the following problem:
- There's a base class
Unit, which has a couple of attributes, e.g.id,type,name,skills, ... - There are different types of units, some of them have additional attributes like
health,attack, ortribe, so naturally the relevant subclassesHealthUnit,AttackUnit, etc. exist as well. - There are a few units that have multiple of these attributes, e.g.
HealthAttackUnit, orHealthAttackTribeUnit.
I want to avoid coding like this:
class Unit(object):
def __init__(self, id, type, name, skills):
self.id= id
self.type= type
self.name= name
self.skills= skills
class HealthUnit(Unit):
def __init__(self, id, type, name, skills, health):
Unit.__init__(self, id, type, name, skills)
self.health= health
class AttackUnit(Unit):
def __init__(self, id, type, name, skills, attack):
Unit.__init__(self, id, type, name, skills)
self.attack= attack
class HealthAttackUnit(HealthUnit, AttackUnit):
def __init__(self, id, type, name, skills, health, attack):
HealthUnit.__init__(self, id, type, name, skills, health)
AttackUnit.__init__(self, id, type, name, skills, attack)
for obvious reasons.
I tried to use dict unpacking as a workaround, kind of like this:
class HealthUnit(Unit):
def __init__(self, health, **args):
Unit.__init__(self, **args)
self.health= health
but even this comes with lots of duplicate code:
class HealthAttackUnit(HealthUnit, AttackUnit):
def __init__(self, health, attack, **args):
HealhUnit.__init__(self, health=health, **args)
AttackUnit.__init__(self, attack=attack, **args)
class HealthAttackTribeUnit(HealthUnit, AttackUnit, TribeUnit):
def __init__(self, health, attack, tribe, **args):
HealhUnit.__init__(self, health=health, **args)
AttackUnit.__init__(self, attack=attack, **args)
TribeUnit.__init__(self, tribe=tribe, **args)
Plus, this will call Unit.__init__ multiple times, which is less than ideal.
So, the question is: Is there a better, less copy/paste, way of doing this?
Update: The dict unpacking is nice and all, but it's still a little annoying having to call all constructors with keyword arguments. I'd prefer a solution without **kwargs, but I'm guessing there probably isn't one?
1 Answer 1
Yes, this is exactly why the super function exists.
Ensure all your __init__ files call super, and Python will work out the MRO for you and call the relevant classes in turn.
class HealthUnit(Unit):
def __init__(self, **kwargs):
self.health = kwargs.pop('health')
super(HealthUnit, self).__init__(**kwargs)
class AttackUnit(Unit):
def __init__(self, **kwargs):
self.attack = kwargs.pop('attack')
super(AttackUnit, self).__init__(**kwargs)
class TribeUnit(Unit):
def __init__(self, **kwargs):
self.tribe = kwargs.pop('tribe')
super(TribeUnit, self).__init__(**kwargs)
class HealthAttackTribeUnit(HealthUnit, AttackUnit, TribeUnit):
pass
See also Python core contributor (and occasional SO poster) Raymond Hettinger's article Super considered super, but note the syntax in that post is for Python 3, there's a separate link to the version 2 code.
5 Comments
kwargs['health'] before calling the superclass, I'd do e.g. kwargs.get('health'), which also allows setting a default. +1 otherwise.**kwargs it will cause a TypeError. Certainly that will happen if you get all the way to object. Best to remove the arguments when you deal with them, I reckon.super does, thank you, and take my upvote, kind sir.Explore related questions
See similar questions with these tags.
super(), you won't have to call each inherited__init__separately - see e.g. stackoverflow.com/q/576169/3001761. You will need to use*args, **kwargs, too.HealthUnitandAttackUnit, you might want to be interested in mixins instead.Unit, instead of just providing health or attack stuff, I don’t think so.HealthUnitandAttackUnitobjects.