0

I'm just trying to put together a simple RPG example in order to learn OOP in Python.

I created a base class CharRace(object). Then I created two derived classes from this, Elf(CharRace) and Dwarf(CharRace). In addition, I have a second base class CharClass(object), with two derived classes: Wizard(CharClass) and Warrior(CharClass).

What I'd like to be able to do is create a new class called NewChar, that I can pass a name, a race, and a class, and it will create the appropriate object that inherits all the properties of both the race and the class.

For example, I want to created a class instance of NewChar with something like this: Player1 = NewChar("Bob", Elf, Wizard) Player2 = NewChar("Phil", Dwarf, Warrior)

I sure there's a smart / correct / scalable way to do this, or perhaps my classes should be organized differently, but I'm having trouble finding it. Any help or advice is appreciated!

class CharRace(object):
    'race for all characters'
    health = 0
    mana = 0
    race = ""
    name = ""
    def __init__(self, health, mana, race, name):
        self.health = health
        self.mana = mana
        self.race = race
        self.name = name
        print ("self = ",self)
    def decreaseHealth (self, amount):
        self.health = self.health - amount
        print ("Decreased health to: ", self.health)
        if (self.health) <= 0:
            print (self.name, "has died!!!")
    def increaseHealth (self, amount):
        self.health = self.health + amount
        print ("Increased health to: ", self.health)
    def printStats(self):
        print()
        print("Stats for",self.name)
        print ("    Race =",self.race)
        print ("    Health =",self.health)
        print ("    Mana =",self.mana)
        CharClass.printStats(self)
    def __str__(self):
        return (self.name)

class CharClass(object):
    'class for all characters'
    weapon = ""
    armor = 0
    def __init__(self, weapon, armor, classname):
        self.weapon = weapon
        self.armor = armor
        self.classname = classname
        print ("self = ",self)   
    def printStats(self):
        print ("    Weapon =",self.weapon)
        print ("    Armor =",self.armor)

# Character Classes
class Warrior(CharClass):
    'warrior class'
    weapon = "Sword"
    armor  = 200
    classname = "Warrior"
    def __init__(self):
        CharClass.__init__(self, self.weapon, self.armor, self.classname)

class Wizard(CharClass):
    'wizard class'
    weapon = "Wand"
    armor  = 100
    classname = "Wizard"
    def __init__(self):
        CharClass.__init__(self, self.weapon, self.armor, self.classname)

# Character Races
class Elf(CharRace):
    'Elf subclass'
    health = 100
    mana = 200
    race = "Elf"
    def __init__(self, name):
        CharRace.__init__(self, self.health, self.mana, self.race, name)

class Dwarf(CharRace):
    'Dwarf subclass'
    health = 200
    mana = 100
    race = "Dwarf"
    def __init__(self, name):
        CharRace.__init__(self, self.health, self.mana, self.race, name)


# This is not an efficient or scalable way to do this....
class ElfWarrior(Elf,Warrior):
    'New character creation'
    def __init__(self, name):
        Elf.__init__(self, name)
        Warrior.__init__(self)

class ElfWizard(Elf,Wizard):
    'New character creation'
    def __init__(self, name):
        Elf.__init__(self, name)
        Wizard.__init__(self)

class DwarfWarrior(Dwarf,Warrior):
    'New character creation'
    def __init__(self, name):
        Dwarf.__init__(self, name)
        Warrior.__init__(self)

class DwarfWizard(Dwarf,Wizard):
    'New character creation'
    def __init__(self, name):
        Dwarf.__init__(self, name)
        Wizard.__init__(self)
  • 3
    You may want to look up composition instead of using inheritance. Alternatively i think you could use a class factory. – M4rtini Jan 15 '16 at 13:14
  • You can create new arbitrary types with arbitrary base classes using [`type()`](https://docs.python.org/3/library/functions.html?highlight=type#type) (e.g. `type('NewChar', (Elf, Warrior, ...), {})`). However I agree with @M4rtini: composition is better for this sort of stuff (especially when things start getting complicated). – Andrea Corbellini Jan 15 '16 at 13:49

1 Answers1

0

What I'd like to be able to do is create a new class called NewChar, that I can pass a name, a race, and a class, and it will create the appropriate object that inherits all the properties of both the race and the class.

If you really want to use this approach, you might try something like this:

def create_character(race, class, name):
    type_name = race.__name__ + class.__name__
    return type(type_name, (race, class), {'name'=name}

Here is a good discussion on the type function, and metaclasses, which you can use to fine-tune this even further.

That said, this really isn't the best approach. Prefer composition to inheritance. What if your character changes class, or you want to dual class?

You can try something like this instead:

class Character(object):

    def __init__(name, race, class):
        self.name = name
        self.race = race.race
        #etc

    def increase_health(amount):
        self.race.increase_health(amount)

Check out the book Design Patterns: Elements of Reusable Object-Oriented Software or Head First Design Patterns to learn more.

Community
  • 1
  • 1
  • Thanks for the advice! I am now trying to get composition to work. Two questions: 1) I think what is still confusing in the Character class you define above, is why do I need to assign all the attributes from the base classes? Isn't that the point of the __init__ functions? 2) None of the base class methods work on the Character class (e.g., y = Character(Elf,Wizard, "Bob"), y.increaseHealth(10). Isn't that the point of the base class, that any subclasses inherit all their methods? – Rhinopotamus Jan 16 '16 at 22:21
  • 1.) I made a silly typo. You're right, you shouldn't need to reassign all the attributes. 2). This is composition now, not inheritance, so you do lose a bit of the convenience. You'd have to define functions to call the member's functions. See edit for clarification. –  Jan 17 '16 at 02:41