2

What are the proper ways of adding a common set of method/properties to subclasses derived from the same class?

Let's say that these are my classes that I need for a videogame based on animals (it's just an example, I am not really writing a game but the idea is the same)

class Animal():
    def __init(self, numlegs):
        self.numlegs = numlegs

class Dog(Animal):
    def __init(self, numlegs):
        Animal.__init__(self, numlegs)

    def bark(self):
        print "Arf!"

class Cat(Animal):
    def __init(self, numlegs):
        Animal.__init__(self, numlegs)

    def playwithmouse(self):
        print "It's fun!"

class Duck(Animal):
    def __init(self, numlegs):
        Animal.__init__(self, numlegs)

    def fly(self):
        print "Weee!"

At a certain point in the game Dog and Duck can be weaponized, and they need to have the same methods: fire(), reload() and properties: ammocount.

I don't think it would be correct to add them in the Animal() class, because they will be needed in a completely different part of the game.

What would be the correct approach to add them?

update I wanted to add that the expected result is some kind of Animal - Weapon Hybrid Class like

class BurtalDuck(Duck):
    fire(): #<- this is somehow added to Duck
        print "ratatatatat!"

What I get from the answers is that if I need the whole set of "weapons/ammos" I can use multiclassing, otherwise composition is the way to go.

yann.kmm
  • 827
  • 7
  • 21

4 Answers4

4

You can take advantage of multiple inheritance in python, like this:

class Weapon():
    def __init__(self):
        self.ammoCount = 0
    def fire():
        print "fire!"
    def reload():
        print "reloading!"

class Dog(Animal, Weapon):
    def __init(self, numlegs):
        Animal.__init__(numlegs)

    def bark(self):
        print "Arf!"

now you can do this:

dog = Dog(4)
dog.fire()

If Dog is only supposed to be an Animal, however, and not an Animal-Weapon-hybrid, then use an instance of Weapon and assign it to the dog as a component:

class Dog(Animal, Weapon):
    def __init(self, numlegs):
        Animal.__init__(numlegs)
        self.weapon = Weapon()

    def bark(self):
        print "Arf!"

You'd have to use dog.weapon.fire() then. This might not fit your concept of the Dogactually being a weapon though but it is weaponized now. :)

If you have a dog instance and you need it to be weaponized on-the-fly, just take advantage of python's dynamic attribute assignments

class Dog(Animal):
    def __init(self, numlegs):
        Animal.__init__(numlegs)

    def bark(self):
        print "Arf!"
#some other code maybe
dog = Dog(4)
#more other code
dog.weapon = Weapon()
#even more code
dog.weapon.fire()

Generally I'd say:

  • If you want Dog or Duck to be a weapon, use inheritance.
  • If you want Dog or Duck to consist of or have one (or more) weapon/s, use components.
Manuzor
  • 1,746
  • 1
  • 18
  • 19
  • Ok, editing comment again, I want to keep the Dog Class a subclass of Animal only, and weaponize them only in another part of my code (imagine for the version 2 of the game where I want to use the same classes without modifying them) – yann.kmm Jun 18 '12 at 16:09
  • I would suggest using interfaces instead, but as far as I know, python does not support that :) – Manuzor Jun 18 '12 at 16:12
  • Thanks for expanding your answer, also, reading Mark Hildreth's link to the question on SO about inheritance VS composition, makes everything more clear. – yann.kmm Jun 18 '12 at 16:41
  • 1
    I am glad I can help. :) using composition instead of inheritance is especially great for decoupling and often used to realize [design patterns](http://en.wikipedia.org/wiki/Design_patterns). I'd definitely have a closer look at that. – Manuzor Jun 18 '12 at 16:48
3

Do this with composition,

class Weapon(object):
    def __init__(self, type):
        self.type = type

class Dog(Animal):
    def __init(self, numlegs):
        Animal.__init__(numlegs)
        self.gun = Weapon("gun")

    def bark(self):
        print "Arf!"
Jakob Bowyer
  • 33,878
  • 8
  • 76
  • 91
2

One possibility: don't add them to the classes at all. Create a "weapon" class that contains all of the info regarding how weapons work, and add an instance of that class to the objects that can be weapons.

Of course, this means a bit rethinking of your architecture, but it's a nice concept to think about. This idea typically goes by the name "component-based". Google for "component-based game objects" and try to find some blog posts/presentations that describe the idea.

Mark Hildreth
  • 42,023
  • 11
  • 120
  • 109
  • by "add an instance" you mean assigning it to some variable in the class? it does not seem very oop (but who I am to say what is oop? =) – yann.kmm Jun 18 '12 at 16:14
  • 1
    This goes down into decision between inheritance (what you're trying to accomplish) and composition (my suggestion). They have their pros and cons, and some bias from those that use them. The following question has a lot more info to look at for deciding what is a good approach: http://stackoverflow.com/questions/49002/prefer-composition-over-inheritance – Mark Hildreth Jun 18 '12 at 16:24
0

Another approach, using mixins:

class Animal(object):
    def __init__(self, n):
        super(Animal,self).__init__()
        if isinstance(n, Animal):  # copy constructor
            self.numlegs = n.numlegs
        else:
            self.numlegs = n
    def do(self):
        pass

class Dog(Animal):
    def do(self):
        print "Arf!"

class Cat(Animal):
    def do(self):
        print "Torturing small animals is fun!"

class Duck(Animal):
    def do(self):
        print "Wheee!"

class Weapon(object):
    def __init__(self):
        super(Weapon,self).__init__()
        self.ammocount = 7

    def fire(self):
        if self.ammocount > 0:
            print "Bang!"
            self.ammocount -= 1
        else:
            print "<click>"

    def reload(self):
        self.ammocount = 7

class WeaponizedDog(Dog, Weapon): pass
class WeaponizedCat(Cat, Weapon): pass
class WeaponizedDuck(Duck, Weapon): pass

max = Dog(4)                # max is a 4-legged dog
max.do()                    # "Arf!"
max = WeaponizedDog(max)    # If I put my hands in my pockets, I'd be carrying concealed deadly weapons...
max.fire()                  # "Bang!"
Hugh Bothwell
  • 55,315
  • 8
  • 84
  • 99