들어가며
객체지향 프로그래밍(OOP)은 대규모 프로젝트를 체계적으로 구조화하는 핵심 패러다임입니다. 클래스, 객체, 상속, 다형성 등의 개념을 통해 더 모듈화되고 유지보수가 쉬운 코드를 작성할 수 있습니다.
클래스와 객체
예제 1: 기본 클래스 정의
class Dog:
"""개를 표현하는 클래스"""
def __init__(self, name, age):
"""생성자"""
self.name = name
self.age = age
def bark(self):
"""짖기"""
return f"{self.name}가 멍멍합니다"
def age_in_human_years(self):
"""개 나이를 인간 나이로 변환"""
return self.age * 7
# 객체 생성
dog1 = Dog("Buddy", 3)
dog2 = Dog("Max", 5)
print(f"강아지1: {dog1.name}, {dog1.age}세")
print(dog1.bark())
print(f"개의 나이(인간): {dog1.age_in_human_years()}세")
print(f"\n강아지2: {dog2.name}, {dog2.age}세")
print(dog2.bark())
출력:
강아지1: Buddy, 3세
Buddy가 멍멍합니다
개의 나이(인간): 21세
강아지2: Max, 5세
Max가 멍멍합니다
예제 2: 속성과 메서드
class Person:
"""사람을 표현하는 클래스"""
# 클래스 변수
species = "Homo sapiens"
def __init__(self, name, age):
# 인스턴스 변수
self.name = name
self.age = age
def introduce(self):
"""자기소개"""
return f"안녕하세요, 저는 {self.name}이고 {self.age}살입니다"
def have_birthday(self):
"""생일을 맞이"""
self.age += 1
return f"{self.name}이 {self.age}살이 되었습니다"
# 사용
person = Person("Alice", 25)
print(person.introduce())
print(f"종: {person.species}")
print(person.have_birthday())
print(person.introduce())
출력:
안녕하세요, 저는 Alice이고 25살입니다
종: Homo sapiens
Alice이 26살이 되었습니다
안녕하세요, 저는 Alice이고 26살입니다
상속
예제 3: 기본 상속
# 부모 클래스
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return f"{self.name}이 소리를 냅니다"
def move(self):
return f"{self.name}이 움직입니다"
# 자식 클래스
class Cat(Animal):
def speak(self):
"""메서드 오버라이딩"""
return f"{self.name}이 야옹합니다"
class Dog(Animal):
def speak(self):
return f"{self.name}이 멍멍합니다"
# 사용
animals = [
Cat("Whiskers"),
Dog("Buddy"),
Animal("Unknown")
]
for animal in animals:
print(animal.speak())
print(animal.move())
print()
출력:
Whiskers이 야옹합니다
Whiskers이 움직입니다
Buddy이 멍멍합니다
Buddy이 움직입니다
Unknown이 소리를 냅니다
Unknown이 움직입니다
예제 4: super() 사용
class Vehicle:
def __init__(self, brand, model):
self.brand = brand
self.model = model
def get_info(self):
return f"{self.brand} {self.model}"
class Car(Vehicle):
def __init__(self, brand, model, doors):
super().__init__(brand, model) # 부모 클래스 호출
self.doors = doors
def get_info(self):
parent_info = super().get_info()
return f"{parent_info}, {self.doors}도어"
# 사용
car = Car("Tesla", "Model 3", 4)
print(car.get_info())
출력:
Tesla Model 3, 4도어
특수 메서드
예제 5: 매직 메서드
class Book:
def __init__(self, title, author, pages):
self.title = title
self.author = author
self.pages = pages
def __str__(self):
"""print()에서 사용"""
return f"{self.title} by {self.author}"
def __repr__(self):
"""인터프리터에서 사용"""
return f"Book('{self.title}', '{self.author}', {self.pages})"
def __len__(self):
"""len()에서 사용"""
return self.pages
def __eq__(self, other):
"""== 비교"""
if isinstance(other, Book):
return self.title == other.title
return False
def __lt__(self, other):
"""< 비교"""
if isinstance(other, Book):
return self.pages < other.pages
return False
book1 = Book("1984", "George Orwell", 328)
book2 = Book("Brave New World", "Aldous Huxley", 311)
print(f"str: {book1}")
print(f"repr: {repr(book1)}")
print(f"len: {len(book1)} pages")
print(f"book1 == book2: {book1 == book2}")
print(f"book2 < book1: {book2 < book1}")
출력:
str: 1984 by George Orwell
repr: Book('1984', 'George Orwell', 328)
len: 328 pages
book1 == book2: False
book2 < book1: True
정적 메서드와 클래스 메서드
예제 6: @staticmethod와 @classmethod
class Math:
"""수학 관련 유틸리티"""
PI = 3.14159
@staticmethod
def add(a, b):
"""정적 메서드 - 인스턴스 필요 없음"""
return a + b
@classmethod
def from_diameter(cls, diameter):
"""클래스 메서드 - 클래스 정보 사용"""
return cls.PI * (diameter / 2) ** 2
# 사용
print(f"정적 메서드: 3 + 5 = {Math.add(3, 5)}")
print(f"클래스 메서드: 반지름 5인 원의 넓이 = {Math.from_diameter(10):.2f}")
# 인스턴스로도 호출 가능
m = Math()
print(f"인스턴스로 호출: {m.add(10, 20)}")
출력:
정적 메서드: 3 + 5 = 8
클래스 메서드: 반지름 5인 원의 넓이 = 78.54
인스턴스로 호출: 30
흔한 실수와 해결책
실수 1: self 매개변수 누락
틀린 예제:
class Counter:
def __init__(self):
self.count = 0
def increment(): # self 누락!
self.count += 1
올바른 예제:
class Counter:
def __init__(self):
self.count = 0
def increment(self):
self.count += 1
return self.count
실수 2: 클래스 변수와 인스턴스 변수 혼동
틀린 예제:
class Config:
settings = {}
def __init__(self, key, value):
self.settings[key] = value
c1 = Config("debug", True)
c2 = Config("timeout", 30)
print(Config.settings) # {'debug': True, 'timeout': 30}
# 모든 인스턴스가 같은 딕셔너리 공유!
올바른 예제:
class Config:
def __init__(self, key, value):
if not hasattr(self, 'settings'):
self.settings = {}
self.settings[key] = value
c1 = Config("debug", True)
c2 = Config("timeout", 30)
print(c1.settings) # {'debug': True}
print(c2.settings) # {'timeout': 30}
실수 3: 상속 시 super() 호출 누락
틀린 예제:
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def __init__(self, name, breed):
self.breed = breed
# super().__init__(name) 누락!
dog = Dog("Buddy", "Golden")
print(dog.name) # AttributeError!
올바른 예제:
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name)
self.breed = breed
dog = Dog("Buddy", "Golden")
print(f"{dog.name} ({dog.breed})")
연습 문제
문제 1: 은행 계좌 클래스
입금, 출금, 잔액 조회 기능을 가진 BankAccount 클래스를 작성하세요.
문제 2: 상품 클래스
상품의 이름, 가격, 재고를 관리하고 할인을 적용하는 Product 클래스를 작성하세요.
문제 3: 도형 상속
부모 클래스 Shape과 자식 클래스 Circle, Rectangle을 작성하세요.
풀이
문제 1 풀이
class BankAccount:
def __init__(self, owner, balance=0):
self.owner = owner
self.balance = balance
def deposit(self, amount):
if amount > 0:
self.balance += amount
return f"{amount}원을 입금했습니다"
return "잘못된 금액입니다"
def withdraw(self, amount):
if amount > self.balance:
return "잔액 부족"
self.balance -= amount
return f"{amount}원을 출금했습니다"
def get_balance(self):
return f"현재 잔액: {self.balance}원"
account = BankAccount("Alice", 10000)
print(account.deposit(5000))
print(account.withdraw(3000))
print(account.get_balance())
문제 2 풀이
class Product:
def __init__(self, name, price, stock):
self.name = name
self.price = price
self.stock = stock
def apply_discount(self, discount_rate):
return self.price * (1 - discount_rate)
def get_info(self):
return f"{self.name}: {self.price}원 (재고: {self.stock}개)"
product = Product("노트북", 100000, 5)
print(product.get_info())
print(f"10% 할인: {product.apply_discount(0.1)}원")
문제 3 풀이
import math
class Shape:
def area(self):
raise NotImplementedError
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return math.pi * self.radius ** 2
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
circle = Circle(5)
rect = Rectangle(10, 20)
print(f"원의 넓이: {circle.area():.2f}")
print(f"직사각형의 넓이: {rect.area()}")
마무리
객체지향 프로그래밍을 마스터하면 복잡한 프로젝트도 체계적으로 구조화할 수 있습니다. 각 개념을 충분히 이해하고 실제 프로젝트에 적용해보세요.