8. Classes & Objects

This is a big one — but it’s also the most powerful idea in the whole handbook. Take it slowly. Once it clicks, your evolution sim will make total sense.


The big idea: a blueprint and the things made from it

Imagine a cookie cutter. The cutter itself isn’t a cookie — it’s the shape that makes cookies. You can stamp out as many cookies as you like, and they all share the same shape, but each one is its own separate cookie.

In programming:

  • The cookie cutter is a class — the blueprint.
  • Each cookie is an object (also called an instance) — a real thing made from the blueprint.

For your sim, you’ll make a Creature class (the blueprint), and then create lots of creature objects from it. Each creature is separate, with its own energy and position, but they all follow the same blueprint.


The two things a class holds

A class describes two kinds of things about your object:

  1. Properties — the things an object has (its data). A creature has a name, energy, and speed. These are also called attributes.
  2. Methods — the things an object can do (its actions). A creature can move, eat, and have a baby. A method is just a function that lives inside a class.

Making your first class

Let’s build a Creature, one step at a time.

Step 1: the class and its setup

class Creature:
    def __init__(self, name, speed):
        self.name = name
        self.energy = 100
        self.speed = speed

There’s a lot happening here, so let’s go through it slowly.

  • class Creature: — this starts the blueprint. Class names start with a Capital Letter (this is called PascalCase). That’s how you tell a class apart from a variable.

  • def __init__(self, ...) — this special method is the setup instructions. It runs automatically every time you make a new creature. The name is __init__ — that’s two underscores on each side. (People say it out loud as “dunder init”, short for “double underscore init”.) Its job is to get a fresh creature ready.

  • self — this word means “this particular creature”. Since all creatures share the same blueprint, self is how each one keeps track of its own data. Every method’s first parameter is self.

  • self.name = name — this stores the name we were given onto this creature. self.energy = 100 gives every new creature 100 energy to start. These self.something values are the properties (attributes) — the creature’s data.

Step 2: make some creatures

Now we use the blueprint to make real creatures:

spike = Creature("Spike", 5)
bramble = Creature("Bramble", 3)

When you write Creature("Spike", 5), Python runs __init__ for you, with name set to "Spike" and speed set to 5. (You never pass in self yourself — Python fills that in automatically.)

Now spike and bramble are two separate creature objects.

Step 3: read a creature’s properties

Use a dot . to reach a creature’s properties:

print(spike.name)      # Spike
print(spike.energy)    # 100
print(bramble.speed)   # 3

Output:

Spike
100
3

Each creature has its own data. Changing one doesn’t change the other:

spike.energy = 50           # only Spike loses energy
print(spike.energy)         # 50
print(bramble.energy)       # 100 — Bramble is untouched

Adding methods (things the creature can DO)

A method is a function inside the class. Its first parameter is always self, so it can see and change this creature’s own data.

Let’s give our creature the ability to move and to eat:

class Creature:
    def __init__(self, name, speed):
        self.name = name
        self.energy = 100
        self.speed = speed

    def move(self):
        # moving costs energy
        self.energy -= self.speed
        print(f"{self.name} moves and now has {self.energy} energy.")

    def eat(self, food_energy):
        # eating gains energy
        self.energy += food_energy
        print(f"{self.name} eats and now has {self.energy} energy.")

Now watch a creature live its little life:

spike = Creature("Spike", 5)

spike.move()        # costs 5 energy
spike.move()        # costs another 5
spike.eat(20)       # gains 20 energy

Output:

Spike moves and now has 95 energy.
Spike moves and now has 90 energy.
Spike eats and now has 110 energy.

See how spike.move() used self inside the method to change Spike’s energy? When you call spike.move(), Python quietly sends spike in as self. That’s the magic link.


A method that answers a question

Methods can return answers, just like normal functions:

class Creature:
    def __init__(self, name, speed):
        self.name = name
        self.energy = 100
        self.speed = speed

    def is_alive(self):
        return self.energy > 0
spike = Creature("Spike", 5)
spike.energy = 0

if spike.is_alive():
    print("Spike is alive.")
else:
    print("Spike has died.")

Output:

Spike has died.

Why this is perfect for an evolution sim

Here’s where it all comes together. You keep all your creatures in a list, then use a for loop to make every one of them take a turn:

class Creature:
    def __init__(self, name, speed):
        self.name = name
        self.energy = 100
        self.speed = speed

    def move(self):
        self.energy -= self.speed

    def is_alive(self):
        return self.energy > 0


# a whole population of creatures, each its own object
creatures = [
    Creature("Spike", 5),
    Creature("Bramble", 3),
    Creature("Pip", 8),
]

# one step of the world: everyone moves
for creature in creatures:
    creature.move()
    print(f"{creature.name} now has {creature.energy} energy.")

Output:

Spike now has 95 energy.
Bramble now has 97 energy.
Pip now has 92 energy.

Look how tidy that is! Whether you have 3 creatures or 3,000, that same little loop handles them all. That’s the power of classes.


A quick word on the names

  • Class names use Capitals: Creature, FoodItem, World.
  • Property and method names use snake_case: self.energy, def move(self):.
  • self is always the first parameter of every method.
  • __init__ has two underscores on each side.

✅ Quick recap

  • A class is a blueprint; an object is a real thing made from it.
  • Properties (attributes) are what an object has: self.energy.
  • Methods are what an object can do: def move(self):.
  • __init__ is the setup method that runs when you make a new object.
  • self means “this particular object” — it’s how each object keeps its own data.
  • Make an object with spike = Creature("Spike", 5).
  • Reach its data with a dot: spike.energy.
  • Keep many objects in a list and loop over them to make your world tick.