Object-oriented Python and maintainable design · Lesson 1 of 3
Classes, objects, and instance state
Define a class when data and behavior form a useful object contract.
Lesson
1. Concept and mental model
A class defines behavior and structure; an instance is an object created from that class. The __init__ method initializes instance state, and self refers to the particular object being used. Instance attributes belong to each object, while class attributes are shared through the class unless shadowed. A mutable class attribute can unintentionally share data across all instances, so put per-instance lists or dictionaries on self during initialization.
2. How it works
Classes are useful when an object has a stable identity, meaningful state, and operations that belong together. They are not automatically better than simple functions or dictionaries. Start with the simplest representation that makes valid states and behavior clear. Use properties or methods to enforce invariants when callers should not modify an internal value arbitrarily.
3. Use it safely and effectively
Prefer composition when one object can use another object’s capabilities without inheriting its entire implementation. Inheritance expresses an “is-a” relationship and should preserve the expectations of the base type. Keep constructors predictable; avoid hidden network or file access inside __init__, because it makes object creation slow and hard to test. Practice by modeling a bank account that validates deposits and does not allow an invalid balance transition.
4. Syntax reference
class Counter:
def __init__(self) -> None:
self.value = 0
def increment(self) -> int:
self.value += 1
return self.value
print(Counter().increment())5. Guided walkthrough
Define the state each object owns, initialize it in __init__, and let methods operate on that instance through self. Create two objects with different values and verify that updating one does not unexpectedly change the other.
6. Common mistakes to avoid
- Putting a mutable per-instance value on the class, which makes instances share the same object.
- Using a class for a group of unrelated functions and data that do not need an object contract.
Visual guide
Create and use an object
-
Step 01
Define
Describe attributes and methods on a class.
-
Step 02
Instantiate
Initialize state for one object.
-
Step 03
Operate
Call methods that preserve its invariants.
Code examples
class Counter:
def __init__(self) -> None:
self.value = 0
def increment(self) -> int:
self.value += 1
return self.value
print(Counter().increment())
1
class Playlist:
def __init__(self, name: str) -> None:
self.name = name
self.songs: list[str] = []
first = Playlist('Morning')
first.songs.append('Intro')
print(first.songs)
['Intro']
class Rectangle:
def __init__(self, width: float, height: float) -> None:
self.width = width
self.height = height
def area(self) -> float:
return self.width * self.height
print(Rectangle(3, 4).area())
12
Key points
- Define a class when data and behavior form a useful object contract.
- Prefer explicit, testable behavior over hidden side effects.
- Validate assumptions at system boundaries and handle failure deliberately.
Check your understanding
Practice
Your learning progress