Object-Oriented Programming
🏛️ Object-Oriented Programming
__init__ constructor, the self parameter, and the difference between instance attributes and class attributes.
🧱 Defining a Class
__init__ is the constructor — it runs automatically when Dog(...) is called, setting up whatever data a new object needs. By convention, class names use PascalCase, distinguishing them at a glance from the snake_case functions and variables you've used since Course 1.
👤 self Explained
self refers to the specific object a method is being called on — it's always the first parameter of every method, but you never pass it explicitly; Python fills it in automatically:
The "this" Parameter: Go vs Kotlin vs Python
| Language | How the current object is referenced |
|---|---|
| Go | No classes at all — a method has an explicit receiver, e.g. func (d Dog) Bark() string, conceptually similar to self but attached to the function signature differently, and structs/methods are separate constructs rather than one class. |
| Kotlin | this is implicit — never declared as a parameter, just used directly inside a method when needed, and usually omittable entirely (name instead of this.name). |
| Python | self must be declared explicitly as the first parameter of every method, and used explicitly (self.name) to access instance attributes — nothing is implicit here. |
Coming from Kotlin, writing out self everywhere feels verbose at first — it's a deliberate Python design choice ("explicit is better than implicit"), not an oversight.
📌 Instance Attributes vs Class Attributes
An instance attribute (set via self.x = ..., usually in __init__) belongs to one specific object. A class attribute (defined directly in the class body) is shared by every instance of that class — change it via the class itself, and every instance sees the update.
This is the same root cause as Course 1's mutable-default-argument gotcha (Chapter 8) — a single mutable object gets shared where independent copies were expected. The fix: give each instance its own list inside __init__, with self.tricks = [], not as a class attribute.
class / __init__
class Name: defines a type; __init__ runs automatically on creation.
self
Explicit first parameter of every method — refers to the specific object being used.
Instance attributes
self.x = ... — unique data per object, usually set in __init__.
Class attributes
Defined in the class body — shared by every instance; avoid mutable ones.
💻 Coding Challenges
Challenge 1: Bank Account Class
Write a BankAccount class with an __init__ that takes owner and an optional balance (default 0), plus a deposit(amount) method that adds to the balance. Create an account and deposit twice, printing the final balance.
Goal: Practice defining a class with instance attributes and a method that modifies them.
Challenge 2: Class Attribute Counter
Write a Robot class with a class attribute count = 0 that increments by 1 every time a new Robot is created (inside __init__). Create three robots, then print the total count.
Goal: Practice a legitimate, safe use of a class attribute — one that's read/incremented as a number, not a shared mutable container.
Challenge 3: Fix the Shared-List Bug
Given the buggy Dog class from this chapter's warning box (with tricks = [] as a class attribute), rewrite it so each dog's tricks list is independent, and prove the fix by showing fido.tricks stays empty after rex.add_trick("sit").
Goal: Apply the fix for the shared-mutable-class-attribute gotcha directly.
🎯 What's Next
Next chapter: OOP Deep Dive — inheritance, polymorphism, and magic/dunder methods like __str__ and __eq__.