Home/Python/Classes and objects

Classes and objects

Keep related state and behavior together

Classes and instances

A class groups related state and behavior. An instance holds its own attributes; methods operate on that state. Classes are useful when several operations must preserve the same rules.

Construction normally calls __init__, which initializes the instance and must return None. The first method parameter is conventionally self; Python passes the instance automatically in calls such as counter.add(3).

State transitions

Validate before mutation so a rejected operation preserves the previous state:

Python
class Capacity:
    def __init__(self, available):
        if available < 0:
            raise ValueError("negative capacity")
        self.available = available

    def reserve(self, amount):
        if not 0 <= amount <= self.available:
            raise ValueError("reservation exceeds capacity")
        self.available -= amount

pool = Capacity(5)
pool.reserve(3)
try:
    pool.reserve(4)
except ValueError:
    print(pool.available)  # 2

The constructor establishes nonnegative capacity; reserve preserves it. This example assumes numeric input and single-threaded use. Shared access would need the check and update protected together.

Establish instance attributes during construction. Keep one coherent responsibility per class; a collection of unrelated helper methods does not establish a useful state model.

Shared class attributes

Mutating a class-level container affects every instance that still refers to it. Assigning an instance attribute shadows the class attribute instead:

Python
class SharedHooks:
    hooks = []

first = SharedHooks()
second = SharedHooks()
first.hooks.append("audit")
print(second.hooks)  # ['audit']
first.hooks = []
print(first.hooks, second.hooks)  # [] ['audit']

Create mutable per-instance storage in __init__. Shared registries can be intentional, but need explicit ownership and cleanup rules.

Gotchas

  • A leading underscore marks internal state by convention; it does not block access. Direct writes can bypass method validation.
  • Returning an internal list exposes another mutation path. A shallow snapshot protects only the outer container; nested mutable values may still be shared.
  • A read-only property can guide callers toward the intended interface, but does not make the entire object immutable.

Document permitted mutations and snapshot behavior. Test state transitions and failure behavior rather than private attribute names.

Put it into practice

Implement the exercise in a full-screen workspace, then run its tests.

Open practice
Next lessonInheritance and composition