Object-Oriented Programming
๐๏ธ Object-Oriented Programming
__init__ constructor, the self parameter, and the difference between instance attributes and class attributes.
๐งฑ Defining a Class
class Dog: def __init__(self, name, breed): self.name = name self.breed = breed def bark(self): return f"{self.name} says Woof!" rex = Dog("Rex", "Labrador") print(rex.bark()) # Rex says Woof!
__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:
rex.bark() # Python actually calls this as: Dog.bark(rex) # bark(self) receives rex as self
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
class Dog: species = "Canis familiaris" # CLASS attribute โ shared by every Dog def __init__(self, name): self.name = name # INSTANCE attribute โ unique per object rex = Dog("Rex") fido = Dog("Fido") print(rex.name, fido.name) # Rex Fido โ different per instance print(rex.species, fido.species) # Canis familiaris Canis familiaris โ same for both
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.
class Dog: tricks = [] # DANGER: one list, shared by every Dog def __init__(self, name): self.name = name def add_trick(self, trick): self.tricks.append(trick) # mutates the SHARED class list rex = Dog("Rex") fido = Dog("Fido") rex.add_trick("sit") print(fido.tricks) # ['sit'] โ Fido "learned" Rex's trick!
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__.