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:
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) # 2The 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:
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.