homework.wenqian.dev
< Back to index
Tutorial 22026-07-02

Python Class Starter

class · object · self · attributes · methods · calculator · small battle project

What A Class Solves

Core idea: A class keeps related data and actions in one place. Instead of passing name, hp, attack_power, and items everywhere, one object can carry its own state and behavior.

class

The blueprint. It describes what data and methods this kind of object should have.

object

One real thing created from a class. hero and enemy can both be Player objects.

__init__

The setup method. Python runs it when you create a new object.

self

The current object. self.hp means this object's hp.

attribute

Data stored inside an object, such as hero.hp or bag.items.

method

A function that belongs to an object, such as hero.take_damage(5).

First Classes: Student and Calculator

Run this first

python
class Student:    def __init__(self, name, energy):        self.name = name        self.energy = energy    def study(self):        self.energy -= 10        return f"{self.name} studied. Energy: {self.energy}"    def rest(self):        self.energy += 20        return f"{self.name} rested. Energy: {self.energy}"student = Student("Mia", 80)print(student.name)print(student.energy)print(student.study())print(student.rest())print(student.energy)

Trace the object state

python
student = Student("Mia", 80)# After __init__:# student.name   -> "Mia"# student.energy -> 80student.study()# After study:# student.name   -> "Mia"# student.energy -> 70student.rest()# After rest:# student.name   -> "Mia"# student.energy -> 90

Calculator methods: add, subtract, multiply, divide

python
class Calculator:    def __init__(self, start):        self.value = start    def add(self, number):        self.value += number        return self.value    def subtract(self, number):        self.value -= number        return self.value    def multiply(self, number):        self.value *= number        return self.value    def divide(self, number):        if number == 0:            return "Cannot divide by zero"        self.value /= number        return self.valuecalc = Calculator(10)print(calc.add(5))       # 15print(calc.subtract(3))  # 12print(calc.multiply(2))  # 24print(calc.divide(4))    # 6.0print(calc.divide(0))    # Cannot divide by zeroprint(calc.value)        # 6.0

Trace calculator.value

python
calc = Calculator(10)# After __init__:# calc.value -> 10calc.add(5)# calc.value -> 15calc.subtract(3)# calc.value -> 12calc.multiply(2)# calc.value -> 24calc.divide(4)# calc.value -> 6.0calc.divide(0)# calc.value -> still 6.0
Teaching note: Pause after each method call and ask: which object changed, which attribute changed, what parameter was passed in, and what value was returned?

Mini Project: Class Arena

Students complete a tiny battle simulator. The point is not game design; the point is that Player owns hp and attack behavior, Inventory owns item behavior, and GameStats owns the counters.

class_arena.py

python
class Player:    def __init__(self, name, hp, attack_power):        # TODO 1: Store the player's starting data.        # Input:        #   name         - a string, for example "Nova"        #   hp           - an integer, for example 30        #   attack_power - an integer, for example 7        # Output:        #   No return value.        # Task:        #   Create these attributes:        #     self.name        #     self.max_hp        #     self.hp        #     self.attack_power        pass    def is_alive(self):        # TODO 2: Check whether this player is still alive.        # Input:        #   No parameters besides self.        # Output:        #   True if self.hp is greater than 0.        #   False otherwise.        return False    def take_damage(self, amount):        # TODO 3: Reduce hp.        # Input:        #   amount - an integer damage value.        # Output:        #   No return value.        # Task:        #   Subtract amount from self.hp.        #   If hp becomes negative, set it back to 0.        pass    def heal(self, amount):        # TODO 4: Restore hp.        # Input:        #   amount - an integer healing value.        # Output:        #   No return value.        # Task:        #   Add amount to self.hp.        #   If hp becomes greater than self.max_hp, set it back to self.max_hp.        pass    def attack_target(self, target):        # TODO 5: Attack another Player object.        # Input:        #   target - another Player object.        # Output:        #   No return value.        # Task:        #   Call target.take_damage(self.attack_power).        pass    def status(self):        # TODO 6: Return a readable status string.        # Input:        #   No parameters besides self.        # Output:        #   A string.        # Example:        #   "Nova: 24/30 HP, ATK 7"        return ""class Inventory:    def __init__(self):        # TODO 7: Create an empty item list.        # Input:        #   No parameters besides self.        # Output:        #   No return value.        # Task:        #   Create self.items as an empty list.        pass    def add(self, item):        # TODO 8: Add one item to the inventory.        # Input:        #   item - a string, for example "potion"        # Output:        #   No return value.        pass    def has(self, item):        # TODO 9: Check whether an item exists.        # Input:        #   item - a string.        # Output:        #   True if item is inside self.items.        #   False otherwise.        return False    def remove(self, item):        # TODO 10: Remove one item if it exists.        # Input:        #   item - a string.        # Output:        #   True if an item was removed.        #   False if the item was not found.        return False    def summary(self):        # This one is already done.        if len(self.items) == 0:            return "Bag: empty"        return "Bag: " + ", ".join(self.items)class GameStats:    def __init__(self):        # This one is already done.        self.rounds = 0        self.actions = 0    def record_round(self):        # This one is already done.        self.rounds += 1    def record_action(self):        # This one is already done.        self.actions += 1    def result(self, hero, enemy):        # This one is already done.        if hero.is_alive():            winner = hero.name        else:            winner = enemy.name        return f"Winner: {winner} | Rounds: {self.rounds} | Actions: {self.actions}"def play_demo():    hero = Player("Nova", hp=30, attack_power=7)    enemy = Player("Slime", hp=25, attack_power=8)    bag = Inventory()    stats = GameStats()    if hero.status() == "":        print("Template loaded. Start with TODO 1-6 inside Player.")        return    if not hasattr(bag, "items"):        print("Player is ready. Next finish TODO 7-10 inside Inventory.")        return    bag.add("potion")    print(hero.status())    print(enemy.status())    print(bag.summary())    while hero.is_alive() and enemy.is_alive():        stats.record_round()        print()        print(f"Round {stats.rounds}")        hero.attack_target(enemy)        stats.record_action()        print("Hero attacks.")        print(enemy.status())        if not enemy.is_alive():            break        enemy.attack_target(hero)        stats.record_action()        print("Enemy attacks.")        print(hero.status())        if hero.hp <= 12 and bag.has("potion"):            bag.remove("potion")            hero.heal(10)            stats.record_action()            print("Hero uses potion.")            print(hero.status())            print(bag.summary())    print()    print(stats.result(hero, enemy))if __name__ == "__main__":    play_demo()

Quick smoke test

bash
python3 class_arena.py# If the program is working, you should see:# - Nova and Slime status lines# - Round 1, Round 2, ...# - HP going down after attacks# - potion used when Nova is low# - a final Winner line

Practice Ladder

Practice 1

Trace an object

Write down how self.energy changes after each method call.

Input

python
student = Student("Mia", 80)student.study()student.rest()

Expected Output

plain
student.name == "Mia"student.energy == 90
Practice 2

Fix a missing self bug

Explain why name = name does not create hero.name, then fix the code.

Input

python
class Player:    def __init__(self, name, hp):        name = name        hp = hphero = Player("Nova", 30)print(hero.name)

Expected Output

plain
Nova
Practice 3

Build a Rectangle class

Create __init__, area, and perimeter.

Input

python
box = Rectangle(3, 5)print(box.area())print(box.perimeter())

Expected Output

plain
1516
Practice 4

Build a BankAccount class

withdraw should return False when there is not enough money.

Input

python
account = BankAccount("Mia", 100)print(account.withdraw(30))print(account.balance)print(account.withdraw(1000))

Expected Output

plain
True70False
Practice 5

Finish Player.take_damage

Damage should never make hp negative.

Input

python
hero = Player("Nova", 30, 7)hero.take_damage(40)print(hero.hp)

Expected Output

plain
0
Practice 6

Finish Player.heal

Healing should never go above max_hp.

Input

python
hero = Player("Nova", 30, 7)hero.take_damage(20)hero.heal(99)print(hero.hp)

Expected Output

plain
30
Practice 7

Use a list attribute

Implement items as self.items, not as a local variable.

Input

python
bag = Inventory()bag.add("potion")bag.add("key")print(bag.has("key"))print(bag.summary())

Expected Output

plain
TrueBag: potion, key
Practice 8

Remove exactly one item

remove should delete one matching item and return True.

Input

python
bag = Inventory()bag.add("potion")bag.add("potion")print(bag.remove("potion"))print(bag.summary())

Expected Output

plain
TrueBag: potion
Practice 9

Loop over objects

Use a for loop and player.is_alive() to count how many players are alive.

Input

python
players = [Player("Nova", 30, 7), Player("Slime", 0, 4)]# Count alive players.

Expected Output

plain
1
Practice 10

Add a level_up method

Add level_up so max_hp increases by 5 and attack_power increases by 2.

Input

python
hero = Player("Nova", 30, 7)hero.level_up()print(hero.max_hp)print(hero.attack_power)

Expected Output

plain
359
Practice 11

Build Calculator.__init__

Create self.value and store the starting number inside the object.

Input

python
calc = Calculator(10)print(calc.value)

Expected Output

plain
10
Practice 12

Add and subtract

Make add and subtract update self.value and return the new value.

Input

python
calc = Calculator(10)print(calc.add(5))print(calc.subtract(8))print(calc.value)

Expected Output

plain
1577
Practice 13

Multiply and divide

Make multiply and divide update self.value and return the new value.

Input

python
calc = Calculator(6)print(calc.multiply(3))print(calc.divide(2))

Expected Output

plain
189.0
Practice 14

Protect against dividing by zero

If number is 0, return an error message and do not change self.value.

Input

python
calc = Calculator(9)print(calc.divide(0))print(calc.value)

Expected Output

plain
Cannot divide by zero9

Common Bug: Forgetting self

Broken

python
class Player:    def __init__(self, name, hp):        name = name        hp = hphero = Player("Nova", 30)print(hero.name)

Fixed

python
class Player:    def __init__(self, name, hp):        self.name = name        self.hp = hphero = Player("Nova", 30)print(hero.name)
Rule: If the value should stay inside the object after __init__ finishes, store it as self.something.

Where to Slow Down

  • Do not rush past self. Ask students to say out loud: self means this object.
  • Separate local variables from attributes. name disappears after __init__, but self.name stays inside the object.
  • Connect every method to a visible state change: hp decreases, hp heals, items are added, items are removed.