DMarlon Studio

Lesson 8 · 8 of 20

Classes and objects · Python

Object-oriented programming in Python: classes, __init__, self, attributes, methods, properties and __str__, with players in a role-playing game.

Python · Classes and objects in DMarlon Studio: the example's code and its output in the terminal

A class is a mould and each object (or instance) is a piece made from it. Player("Aria", attack=35) creates a new player: Python makes the object and calls `__init__`, which fills in its data.

The first parameter of every method, `self`, is the object itself. self.name = name creates an instance attribute: each player has its own. max_health, on the other hand, sits in the class body and is a class attribute, shared by all of them (Player.max_health).

Methods are functions that work on the object. hit(self, other) takes another player and lowers their health, never below zero thanks to max(0, ...); pick_up adds items to the inventory list, which also belongs to each player because it is created in __init__.

@property turns alive into a computed value you read like an attribute, without parentheses: while orc.alive: keeps hitting until the orc runs out of health. __str__ decides how the object looks in print().

The last line separates two ideas: hero and orc have the same type (type(hero) is type(orc)), but they are different objects (hero is orc is False).

Code · main.py

class Player:    """A player in a small role-playing game."""    max_health = 100                  # class attribute: shared by all    def __init__(self, name, attack):        self.name = name              # instance attributes: one per object        self.attack = attack        self.health = Player.max_health        self.inventory = []    def hit(self, other):        other.health = max(0, other.health - self.attack)        print(f"{self.name} hits {other.name}: {other.health} HP left")    def pick_up(self, item):        self.inventory.append(item)    @property    def alive(self):                  # used like an attribute, no ()        return self.health > 0    def __str__(self):        return f"{self.name} ({self.health}/{self.max_health} HP)"hero = Player("Aria", attack=35)orc = Player("Grok", attack=20)hero.pick_up("potion")while orc.alive:    hero.hit(orc)print(hero, "| inventory:", hero.inventory)print(orc, "| alive:", orc.alive)print("Same class:", type(hero) is type(orc), "| same object:", hero is orc)

Output · python3 main.py

Aria hits Grok: 65 HP left
Aria hits Grok: 30 HP left
Aria hits Grok: 0 HP left
Aria (100/100 HP) | inventory: ['potion']
Grok (0/100 HP) | alive: False
Same class: True | same object: False

Key points

  • class defines the mould; Class(...) creates an object.
  • __init__ sets up each new object; self is that object.
  • Attributes on self belong to each object; those in the class body are shared.
  • @property exposes a calculation as if it were an attribute.
  • __str__ controls what print() shows.

Exercise

Add a heal(amount) method that raises health without going past max_health, and make Grok heal 20 points after the second hit.