Chapter 6: Classes — Blueprints for Objects¶
When Functions Are Not Enough¶
Functions let you reuse code.
But when you want to bundle data and behavior together, you need a class.
# Managing 2 students — name, score, pass/fail separately
name1 = "Alice"; score1 = 88; passed1 = score1 >= 60
name2 = "Bob"; score2 = 45; passed2 = score2 >= 60
For 100 students that's 300 variables.
A class bundles everything into a single Student object.
Key Vocabulary¶
| Term | Meaning | Example |
|---|---|---|
| Class | The blueprint / design document | class Monster: |
| Object | The actual product made from the blueprint | m = Monster("Slime") |
| Attribute | A variable inside a class | self.name, self.hp |
| Method | A function inside a class | def attack(self): |
| Constructor | The first function called when an object is created | def __init__(self): |
Understanding Classes with a Monster Example¶
class Monster: # blueprint
def __init__(self, name, hp): # constructor
self.name = name # attribute
self.hp = hp # attribute
def attack(self): # method
print(f"{self.name} attacks! (HP: {self.hp})")
def is_alive(self): # method
return self.hp > 0
# Create objects from the blueprint
slime = Monster("Slime", 30) # object 1
goblin = Monster("Goblin", 50) # object 2
slime.attack() # Slime attacks! (HP: 30)
goblin.attack() # Goblin attacks! (HP: 50)
print(slime.is_alive()) # True
Both objects (slime, goblin) share the same blueprint (Monster) but have independent attribute values.
What is an Object?¶
Object = attributes (data) + methods (behavior)
Dog object:
attributes: name = "Choco", breed = "Maltese", age = 3
methods: bark(), eat(), sleep()
Everything in the real world can be modeled as attributes and behavior.
Defining a Class¶
Class = a blueprint for creating objects
class Dog:
def __init__(self, name, breed):
self.name = name # instance variable
self.breed = breed
def bark(self):
print(f"{self.name}: Woof!")
# Create objects (instances) from the class
d1 = Dog("Choco", "Maltese")
d2 = Dog("Bori", "Shiba")
d1.bark() # Choco: Woof!
d2.bark() # Bori: Woof!
__init__ — The Constructor¶
Called automatically when an object is created.
Set initial attributes (instance variables) here.
class Circle:
def __init__(self, radius):
self.radius = radius
def area(self):
import math
return math.pi * self.radius ** 2
c = Circle(5)
print(c.radius) # 5
print(round(c.area(), 2)) # 78.54
self — A Reference to the Object Itself¶
Every instance method's first parameter is self.
self refers to the current object.
class Counter:
def __init__(self):
self.count = 0
def increment(self):
self.count += 1
def get(self):
return self.count
c1 = Counter()
c2 = Counter()
c1.increment()
c1.increment()
print(c1.get()) # 2 (only c1 was incremented)
print(c2.get()) # 0 (c2 is untouched)
self.count is stored in a separate space for each object.
Instance Variables vs Class Variables¶
| Instance Variable | Class Variable | |
|---|---|---|
| Defined in | __init__, with self.name |
Class body, without self |
| Shared? | Independent per object | Shared by all objects |
| Access | self.name |
ClassName.name or self.name |
class Student:
school = "Korea High School" # class variable — shared
def __init__(self, name, score):
self.name = name # instance variable
self.score = score
s1 = Student("Alice", 90)
s2 = Student("Bob", 75)
print(s1.school) # Korea High School
Student.school = "Seoul High School"
print(s1.school) # Seoul High School
print(s2.school) # Seoul High School (all changed)
The Class Variable Trap ⚠️¶
class Dog:
tricks = [] # class variable — a shared list!
def add_trick(self, trick):
self.tricks.append(trick)
d1 = Dog(); d2 = Dog()
d1.add_trick("sit")
d2.add_trick("shake")
print(d1.tricks) # ['sit', 'shake'] <- d2's trick shows up in d1!
print(d2.tricks) # ['sit', 'shake']
Fix: Create the list as an instance variable inside __init__
class Dog:
def __init__(self):
self.tricks = [] # separate list for each object
def add_trick(self, trick):
self.tricks.append(trick)
Special (Magic) Methods¶
Methods named __name__ that Python calls automatically in specific situations.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self): # called by print()
return f"({self.x}, {self.y})"
def __add__(self, other): # called by +
return Point(self.x + other.x, self.y + other.y)
p1 = Point(1, 2)
p2 = Point(3, 4)
print(p1) # (1, 2)
print(p1 + p2) # (4, 6)
Inheritance¶
Create a new class that inherits attributes and methods from an existing class.
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print(f"{self.name}: ...")
class Cat(Animal):
def speak(self): # method overriding
print(f"{self.name}: Meow")
class Dog(Animal):
def speak(self):
print(f"{self.name}: Woof")
animals = [Cat("Nabi"), Dog("Choco"), Animal("?")]
for a in animals:
a.speak()
Nabi: Meow
Choco: Woof
?: ...
super() — Calling the Parent Method¶
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # call parent __init__
self.breed = breed # child's own attribute
def info(self):
return f"{self.name} ({self.breed})"
d = Dog("Choco", "Maltese")
print(d.info()) # Choco (Maltese)
Skipping super().__init__() means the parent's attributes won't be initialized.
Access Control — _ and __¶
| Notation | Meaning | Behavior |
|---|---|---|
name |
Public | Accessible anywhere |
_name |
Protected (convention) | Accessible, but "please don't touch" |
__name |
Name mangling | Renamed to _ClassName__name internally |
class BankAccount:
def __init__(self, balance):
self.__balance = balance # name-mangled
def deposit(self, amount):
if amount > 0:
self.__balance += amount
def get_balance(self):
return self.__balance
acc = BankAccount(1000)
# print(acc.__balance) # AttributeError!
acc.deposit(500)
print(acc.get_balance()) # 1500
Practice Missions¶
Mission 1: Rectangle Class¶
class Rectangle:
def __init__(self, width, height):
pass
def area(self):
pass
def perimeter(self):
pass
def __str__(self):
# Return "Rectangle(3x5)"
pass
r = Rectangle(3, 5)
print(r) # Rectangle(3x5)
print(r.area()) # 15
print(r.perimeter()) # 16
Mission 2: Bank Account¶
class BankAccount:
def __init__(self, owner, balance=0):
pass
def deposit(self, amount): # ignore if amount <= 0
pass
def withdraw(self, amount): # print "Insufficient funds" if over balance
pass
def __str__(self):
pass
acc = BankAccount("Alice", 1000)
acc.deposit(500)
acc.withdraw(200)
print(acc) # Alice: 1300
acc.withdraw(2000) # Insufficient funds
Mission 3: Animal Inheritance¶
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def info(self):
return f"{self.name} (age {self.age})"
class Dog(Animal):
def __init__(self, name, age, breed):
# use super()
pass
def speak(self):
return f"{self.name}: Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name}: Meow~"
d = Dog("Choco", 3, "Maltese")
c = Cat("Nabi", 5)
print(d.info()) # Choco (age 3)
print(d.speak()) # Choco: Woof!
print(c.speak()) # Nabi: Meow~
Mission 4 (Advanced): Grade Manager¶
class Student:
student_count = 0 # class variable: total number of students
def __init__(self, name):
self.name = name
self.scores = {}
Student.student_count += 1
def add_score(self, subject, score):
self.scores[subject] = score
def average(self):
if not self.scores:
return 0
return sum(self.scores.values()) / len(self.scores)
def __str__(self):
return f"{self.name}: avg {self.average():.1f}"
s1 = Student("Alice")
s1.add_score("Math", 90)
s1.add_score("English", 85)
s2 = Student("Bob")
s2.add_score("Math", 70)
print(s1) # Alice: avg 87.5
print(Student.student_count) # 2
Key Summary¶
| Concept | Description |
|---|---|
class |
Blueprint for creating objects |
__init__ |
Constructor — called automatically when an object is created |
self |
Reference to the current object |
| Instance variable | self.name — independent per object |
| Class variable | Defined in class body — shared by all objects |
| Class variable trap | Mutable objects (lists etc.) belong in __init__ |
__str__ |
String representation called by print() |
| Inheritance | class Child(Parent): — inherit parent's features |
super() |
Call a method from the parent class |
__name |
Name mangling — blocks direct external access |