36  Character Classes: Creating Your Own Types

Welcome, brave Python adventurers! Today we begin an exciting new chapter in our coding journey: creating our own types using classes. Just as the world of role-playing games has different character classes (like warriors, mages, and rogues), Python allows us to create our own custom types of objects with classes. Let’s learn how to forge these powerful templates!

36.1 What is a Class?

Think of a class as a blueprint or template for creating objects. Just as all warriors in a game share certain characteristics (like strength and weapon skills), objects created from the same class share the same structure and behaviors.

Let’s create our first class - a simple template for warriors:

class Warrior:
    def __init__(self, name, health):
        self.name = name
        self.health = health

This code creates a new type called Warrior. The special __init__ method (called the constructor) sets up the initial attributes of each warrior we create.

36.2 Creating Objects from Classes

Once we have a class definition, we can create (or “instantiate”) objects from it:

# Create two warrior objects
hero = Warrior("Parzival", 100)
companion = Warrior("Galahad", 95)

# Access their attributes
print(f"{hero.name} has {hero.health} health")
print(f"{companion.name} has {companion.health} health")

This will output:

Parzival has 100 health
Galahad has 95 health

Each object we create from the Warrior class is called an instance, and each has its own set of attributes (name and health in this case).

36.3 Adding More Attributes

We can make our warriors more interesting by giving them more attributes:

class Warrior:
    def __init__(self, name, health, strength, weapon):
        self.name = name
        self.health = health
        self.strength = strength
        self.weapon = weapon

# Create a more detailed warrior
hero = Warrior("Parzival", 100, 15, "Excalibur")

print(f"{hero.name} wields {hero.weapon}")
print(f"Stats: Health={hero.health}, Strength={hero.strength}")

This will output:

Parzival wields Excalibur
Stats: Health=100, Strength=15

36.4 The __init__ Method and self

Let’s break down the special parts of our class:

  1. The __init__ method is called automatically when we create a new warrior
  2. self refers to the specific instance being created
  3. self.name = name stores the name parameter as an attribute of the instance

Here’s another example with default values:

class Mage:
    def __init__(self, name, mana=100, spell="Magic Missile"):
        self.name = name
        self.mana = mana
        self.spell = spell

# Create mages with and without default values
merlin = Mage("Merlin")  # Uses default mana and spell
gandalf = Mage("Gandalf", mana=150, spell="Fireball")

print(f"{merlin.name} knows {merlin.spell} and has {merlin.mana} mana")
print(f"{gandalf.name} knows {gandalf.spell} and has {gandalf.mana} mana")

This will output:

Merlin knows Magic Missile and has 100 mana
Gandalf knows Fireball and has 150 mana

36.5 Creating Multiple Classes

We can create different classes for different types of characters:

class Warrior:
    def __init__(self, name, health=100, weapon="Sword"):
        self.name = name
        self.health = health
        self.weapon = weapon

class Archer:
    def __init__(self, name, arrows=20, bow_type="Longbow"):
        self.name = name
        self.arrows = arrows
        self.bow_type = bow_type

# Create different types of characters
knight = Warrior("Lancelot", weapon="Arondight")
ranger = Archer("Robin", arrows=30, bow_type="Elven Bow")

print(f"{knight.name} carries {knight.weapon}")
print(f"{ranger.name} has {ranger.arrows} arrows for their {ranger.bow_type}")

This will output:

Lancelot carries Arondight
Robin has 30 arrows for their Elven Bow

36.6 Practice Time: Create Your Classes

Now it’s your turn to create some classes! Try these challenges:

  1. Create a Potion class that has attributes for name, healing_power, and cost.

  2. Make a Spell class with attributes for name, mana_cost, and damage.

  3. Design a Quest class that tracks a quest’s name, difficulty, and reward.

Here’s a starting point:

# Challenge 1: Potion Class
class Potion:
    def __init__(self, name, healing_power, cost):
        # Your code here
        pass

# Challenge 2: Spell Class
class Spell:
    # Your code here
    pass

# Challenge 3: Quest Class
class Quest:
    # Your code here
    pass

# Test your classes by creating some objects!

36.7 Common Bugs to Watch Out For

As you begin creating classes, be wary of these common pitfalls:

  1. Forgetting self: Always include self as the first parameter in __init__ and other methods.

  2. Name Conflicts: Don’t use the same name for a class attribute and a parameter:

    class Wrong:
        name = "Default"  # Class attribute
        def __init__(self, name):  # Parameter shadows class attribute
            name = name  # Wrong! Should be self.name = name
  3. Missing Parentheses: When creating objects, don’t forget the parentheses:

    warrior = Warrior  # Wrong! This assigns the class itself
    warrior = Warrior()  # Correct! This creates an instance
  4. Case Sensitivity: Class names should start with a capital letter by convention:

    class warrior:  # Not recommended
    class Warrior:  # Recommended
  5. Attribute Access: You can’t access instance attributes before they’re created:

    class Warrior:
        def __init__(self):
            print(self.health)  # Error! health doesn't exist yet
            self.health = 100  # This creates the attribute

36.8 Conclusion and Further Resources

You’ve taken your first steps into the world of object-oriented programming with Python classes. You now know how to create your own types, give them attributes, and create objects from them.

To learn more about Python classes, check out these excellent resources:

  1. Python’s Official Classes Tutorial
  2. Real Python’s OOP Guide
  3. W3Schools Python Classes

Remember, classes are like magical forges where you create templates for the objects in your programs. Keep practicing, and soon you’ll be crafting complex and powerful objects with ease. In our next lesson, we’ll learn how to add behaviors to our objects using methods!