Topic 5: 클래스 고급 기능 - 더 강력한 객체 🚀
🎯 학습 목표
클래스 변수, 클래스 메서드, 특수 메서드 등 고급 기능을 활용하여 더욱 전문적이고 강력한 클래스를 설계할 수 있습니다.
🏢 클래스 변수 vs 인스턴스 변수
개념 이해
클래스 변수: 모든 객체가 공유하는 변수
인스턴스 변수: 각 객체가 개별적으로 가지는 변수
class Student:
# 클래스 변수 - 모든 학생이 공유
school_name = "파이썬 고등학교"
total_students = 0
def __init__(self, name, grade):
# 인스턴스 변수 - 각 학생마다 다름
self.name = name
self.grade = grade
# 클래스 변수 수정
Student.total_students += 1
def introduce(self):
print(f"{Student.school_name}의 {self.name}입니다!")
# 학생들 생성
kim_cheolsu = Student("김철수", 2)
lee_younghee = Student("이영희", 1)
park_minsu = Student("박민수", 3)
# 클래스 변수는 모든 객체가 공유
print(f"학교명: {Student.school_name}")
print(f"총 학생 수: {Student.total_students}")
kim_cheolsu.introduce() # 파이썬 고등학교의 김철수입니다!
lee_younghee.introduce() # 파이썬 고등학교의 이영희입니다!
실용적인 예시: 은행 시스템
class BankAccount:
# 클래스 변수
bank_name = "파이썬 은행"
interest_rate = 0.03 # 연 이자율 3%
total_accounts = 0
total_balance = 0
def __init__(self, owner, account_number, initial_balance=0):
# 인스턴스 변수
self.owner = owner
self.account_number = account_number
self.balance = initial_balance
# 클래스 변수 업데이트
BankAccount.total_accounts += 1
BankAccount.total_balance += initial_balance
def deposit(self, amount):
self.balance += amount
BankAccount.total_balance += amount
print(f"💰 {amount:,}원 입금 완료! 잔액: {self.balance:,}원")
def withdraw(self, amount):
if amount <= self.balance:
self.balance -= amount
BankAccount.total_balance -= amount
print(f"💸 {amount:,}원 출금 완료! 잔액: {self.balance:,}원")
else:
print("잔액이 부족합니다!")
def apply_interest(self):
"""이자 적용"""
interest = int(self.balance * BankAccount.interest_rate)
self.balance += interest
BankAccount.total_balance += interest
print(f"💎 이자 {interest:,}원 적용! 잔액: {self.balance:,}원")
@staticmethod
def get_bank_info():
"""은행 전체 정보 조회"""
print(f"=== {BankAccount.bank_name} 현황 ===")
print(f"총 계좌 수: {BankAccount.total_accounts}개")
print(f"총 예치금: {BankAccount.total_balance:,}원")
print(f"현재 이자율: {BankAccount.interest_rate*100}%")
# 은행 계좌들 생성
account1 = BankAccount("김철수", "123-456", 100000)
account2 = BankAccount("이영희", "789-012", 500000)
# 거래 진행
account1.deposit(50000)
account2.withdraw(100000)
# 이자 적용
account1.apply_interest()
account2.apply_interest()
# 은행 전체 현황
BankAccount.get_bank_info()
🏭 클래스 메서드 (@classmethod)
클래스 메서드의 특징
클래스 메서드는 cls
매개변수를 받아 클래스 자체에 접근할 수 있습니다.
class Car:
total_cars = 0
fuel_types = ["가솔린", "디젤", "전기", "하이브리드"]
def __init__(self, brand, model, fuel_type):
self.brand = brand
self.model = model
self.fuel_type = fuel_type
Car.total_cars += 1
@classmethod
def get_total_cars(cls):
"""총 자동차 수 반환"""
return cls.total_cars
@classmethod
def get_fuel_types(cls):
"""지원하는 연료 타입들 반환"""
return cls.fuel_types
@classmethod
def create_electric_car(cls, brand, model):
"""전기차 생성 팩토리 메서드"""
return cls(brand, model, "전기")
@classmethod
def change_fuel_policy(cls, new_types):
"""연료 정책 변경"""
cls.fuel_types = new_types
print(f"새로운 연료 정책: {new_types}")
# 일반적인 자동차 생성
car1 = Car("현대", "소나타", "가솔린")
car2 = Car("기아", "K5", "하이브리드")
# 클래스 메서드로 전기차 생성
electric_car = Car.create_electric_car("테슬라", "Model 3")
# 클래스 정보 조회
print(f"총 자동차 수: {Car.get_total_cars()}")
print(f"연료 타입들: {Car.get_fuel_types()}")
# 정책 변경
Car.change_fuel_policy(["전기", "수소", "하이브리드"])
⚙️ 정적 메서드 (@staticmethod)
유틸리티 함수로 활용
정적 메서드는 클래스나 인스턴스 상태에 접근하지 않는 독립적인 함수입니다.
class MathUtils:
PI = 3.14159
@staticmethod
def add(a, b):
"""덧셈"""
return a + b
@staticmethod
def multiply(a, b):
"""곱셈"""
return a * b
@staticmethod
def circle_area(radius):
"""원의 넓이 계산"""
return MathUtils.PI * radius * radius
@staticmethod
def is_even(number):
"""짝수 판별"""
return number % 2 == 0
@staticmethod
def factorial(n):
"""팩토리얼 계산"""
if n <= 1:
return 1
return n * MathUtils.factorial(n - 1)
# 정적 메서드 사용 (객체 생성 없이 사용 가능)
print(f"5 + 3 = {MathUtils.add(5, 3)}")
print(f"반지름 5인 원의 넓이: {MathUtils.circle_area(5)}")
print(f"8이 짝수인가? {MathUtils.is_even(8)}")
print(f"5! = {MathUtils.factorial(5)}")
실용적인 예시: 날짜 유틸리티
class DateUtils:
@staticmethod
def is_leap_year(year):
"""윤년 판별"""
return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
@staticmethod
def days_in_month(year, month):
"""해당 년월의 일수 반환"""
days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
if month == 2 and DateUtils.is_leap_year(year):
return 29
return days[month - 1]
@staticmethod
def format_date(year, month, day):
"""날짜 포맷팅"""
return f"{year:04d}-{month:02d}-{day:02d}"
@staticmethod
def parse_date(date_string):
"""날짜 문자열 파싱"""
parts = date_string.split('-')
return int(parts[0]), int(parts[1]), int(parts[2])
# 날짜 유틸리티 사용
print(f"2024년은 윤년? {DateUtils.is_leap_year(2024)}")
print(f"2024년 2월 일수: {DateUtils.days_in_month(2024, 2)}")
print(f"포맷된 날짜: {DateUtils.format_date(2024, 3, 15)}")
year, month, day = DateUtils.parse_date("2024-12-25")
print(f"파싱 결과: {year}년 {month}월 {day}일")
✨ 특수 메서드 (Magic Methods)
str 메서드
객체를 문자열로 표현할 때 사용됩니다.
class Product:
def __init__(self, name, price, category):
self.name = name
self.price = price
self.category = category
def __str__(self):
"""사용자 친화적인 문자열 표현"""
return f"{self.name} - {self.price:,}원 ({self.category})"
def __repr__(self):
"""개발자용 문자열 표현"""
return f"Product('{self.name}', {self.price}, '{self.category}')"
# 특수 메서드 사용
laptop = Product("게이밍 노트북", 1500000, "전자제품")
print(str(laptop)) # __str__ 호출: 게이밍 노트북 - 1,500,000원 (전자제품)
print(repr(laptop)) # __repr__ 호출: Product('게이밍 노트북', 1500000, '전자제품')
print(laptop) # __str__ 호출 (기본)
len 메서드
len() 함수 호출 시 동작을 정의합니다.
class Playlist:
def __init__(self, name):
self.name = name
self.songs = []
def add_song(self, song):
self.songs.append(song)
print(f"♪ '{song}' 추가됨")
def __len__(self):
"""플레이리스트 곡 수 반환"""
return len(self.songs)
def __str__(self):
return f"플레이리스트 '{self.name}' ({len(self)}곡)"
# 플레이리스트 사용
my_playlist = Playlist("좋아하는 곡들")
my_playlist.add_song("Dynamite")
my_playlist.add_song("Permission to Dance")
my_playlist.add_song("Butter")
print(f"곡 수: {len(my_playlist)}") # __len__ 호출: 곡 수: 3
print(my_playlist) # 플레이리스트 '좋아하는 곡들' (3곡)
비교 특수 메서드
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def __eq__(self, other):
"""== 연산자"""
return self.score == other.score
def __lt__(self, other):
"""< 연산자"""
return self.score < other.score
def __le__(self, other):
"""<= 연산자"""
return self.score <= other.score
def __gt__(self, other):
"""> 연산자"""
return self.score > other.score
def __ge__(self, other):
""">= 연산자"""
return self.score >= other.score
def __str__(self):
return f"{self.name}({self.score}점)"
# 학생들 비교
kim_cheolsu = Student("김철수", 85)
lee_younghee = Student("이영희", 92)
park_minsu = Student("박민수", 85)
print(f"{kim_cheolsu} == {park_minsu}: {kim_cheolsu == park_minsu}") # True (같은 점수)
print(f"{kim_cheolsu} < {lee_younghee}: {kim_cheolsu < lee_younghee}") # True (85 < 92)
print(f"{lee_younghee} > {kim_cheolsu}: {lee_younghee > kim_cheolsu}") # True (92 > 85)
# 리스트 정렬도 가능
students = [kim_cheolsu, lee_younghee, park_minsu]
students.sort() # 점수 순으로 정렬
print("성적순 정렬:", [str(s) for s in students])
🎮 종합 예시: 게임 길드 시스템
class Guild:
# 클래스 변수
total_guilds = 0
max_members = 50
def __init__(self, name, leader):
self.name = name
self.leader = leader
self.members = [leader]
self.level = 1
self.experience = 0
Guild.total_guilds += 1
def add_member(self, member_name):
"""길드원 추가"""
if len(self.members) < Guild.max_members:
self.members.append(member_name)
print(f"✅ {member_name}님이 {self.name} 길드에 가입했습니다!")
return True
else:
print(f"❌ 길드 정원이 가득참 (최대 {Guild.max_members}명)")
return False
def gain_experience(self, exp):
"""경험치 획득"""
self.experience += exp
print(f"🎯 길드 경험치 +{exp} (총 {self.experience})")
# 레벨업 체크
while self.experience >= self.level * 1000:
self.experience -= self.level * 1000
self.level += 1
print(f"🎉 길드 레벨업! Lv.{self.level}")
@classmethod
def get_guild_stats(cls):
"""전체 길드 통계"""
print(f"=== 길드 현황 ===")
print(f"총 길드 수: {cls.total_guilds}")
print(f"길드 최대 인원: {cls.max_members}")
@classmethod
def change_max_members(cls, new_max):
"""최대 인원 정책 변경"""
cls.max_members = new_max
print(f"📋 길드 최대 인원이 {new_max}명으로 변경되었습니다!")
@staticmethod
def calculate_guild_power(level, member_count):
"""길드 전투력 계산"""
return level * 100 + member_count * 50
def __len__(self):
"""길드원 수 반환"""
return len(self.members)
def __str__(self):
power = Guild.calculate_guild_power(self.level, len(self.members))
return f"[{self.name}] Lv.{self.level} | 길드원: {len(self.members)}명 | 전투력: {power}"
def __gt__(self, other):
"""길드 전투력 비교"""
my_power = Guild.calculate_guild_power(self.level, len(self.members))
other_power = Guild.calculate_guild_power(other.level, len(other.members))
return my_power > other_power
# 길드 시스템 사용
guild1 = Guild("드래곤슬레이어", "길드장김철수")
guild2 = Guild("파이썬마스터", "마스터이영희")
# 길드원 추가
guild1.add_member("전사박민수")
guild1.add_member("마법사최지영")
guild2.add_member("궁수정도윤")
# 경험치 획득
guild1.gain_experience(800)
guild1.gain_experience(500) # 레벨업!
guild2.gain_experience(600)
# 길드 정보 출력
print(f"\n{guild1}")
print(f"{guild2}")
# 길드 비교
if guild1 > guild2:
print(f"\n🏆 {guild1.name}이 더 강력합니다!")
else:
print(f"\n🏆 {guild2.name}이 더 강력합니다!")
# 전체 통계
Guild.get_guild_stats()
# 정책 변경
Guild.change_max_members(100)
🚨 자주 발생하는 오류
오류 1: 클래스 변수와 인스턴스 변수 혼동
class Counter:
count = 0 # 클래스 변수
def __init__(self):
# ❌ 잘못된 접근
# count += 1 # NameError!
# ✅ 올바른 접근
Counter.count += 1
# 또는 self.__class__.count += 1
def get_count(self):
return Counter.count
오류 2: 특수 메서드 잘못 정의
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
# ❌ 잘못된 특수 메서드 이름
# def __string__(self): # __str__이 맞음
# return self.name
# ✅ 올바른 특수 메서드
def __str__(self):
return f"{self.name}({self.score}점)"
💡 퀴즈: 클래스 고급 기능 이해도 체크
Q1. 클래스 변수의 특징은?
- 각 객체마다 다른 값을 가진다
- 모든 객체가 같은 값을 공유한다
- 객체를 생성해야 사용할 수 있다
- self로 접근해야 한다
💡 정답 확인
정답: 2번 (모든 객체가 같은 값을 공유한다)
클래스 변수는 클래스에 속하며 모든 인스턴스가 공유하는 변수입니다.
Q2. @staticmethod의 특징은?
- self 매개변수가 필요하다
- cls 매개변수가 필요하다
- 클래스나 인스턴스 없이도 호출 가능하다
- 인스턴스 변수에 접근할 수 있다
💡 정답 확인
정답: 3번 (클래스나 인스턴스 없이도 호출 가능하다)
정적 메서드는 독립적인 함수로, 클래스나 인스턴스 상태에 의존하지 않습니다.
Q3. str 메서드의 역할은?
- 문자열을 입력받는다
- 객체를 문자열로 표현한다
- 문자열의 길이를 반환한다
- 문자열을 비교한다
💡 정답 확인
정답: 2번 (객체를 문자열로 표현한다)
__str__
메서드는 객체를 사용자 친화적인 문자열로 표현할 때 사용됩니다.
✅ 클래스 고급 기능 마스터 체크리스트
✅ 클래스 고급 기능 마스터 체크리스트
🌟 다음 단계 미리보기
클래스의 고급 기능을 익혔으니, 이제 클래스 간의 관계를 다뤄보겠습니다!
다음 토픽에서는:
- 상속(Inheritance): 기존 클래스를 확장하기
- 부모 클래스와 자식 클래스: 코드 재사용과 확장
- 메서드 오버라이딩: 부모의 기능을 자식에 맞게 수정
예를 들어:
class Animal: # 부모 클래스
def __init__(self, name):
self.name = name
def speak(self):
print(f"{self.name}가 소리를 냅니다")
class Dog(Animal): # 자식 클래스
def speak(self): # 메서드 오버라이딩
print(f"{self.name}가 멍멍 짖습니다")
class Cat(Animal): # 자식 클래스
def speak(self): # 메서드 오버라이딩
print(f"{self.name}가 야옹 웁니다")
dog = Dog("뽀삐")
cat = Cat("나비")
dog.speak() # 뽀삐가 멍멍 짖습니다
cat.speak() # 나비가 야옹 웁니다
클래스가 다른 클래스를 기반으로 확장됩니다! 🌱
Last updated on