콘텐츠로 이동

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