왜 클래스가 필요한가?
사용자 정보를 딕셔너리로 관리하면:
user = {"name": "철수", "age": 25, "email": "kim@example.com"}
# 나이를 업데이트하는 함수를 별도로 관리해야 함
def update_age(user, new_age):
if new_age < 0:
raise ValueError("나이는 0 이상")
user["age"] = new_age
클래스를 쓰면 데이터와 관련 기능을 하나로 묶을 수 있습니다.
클래스 기본 구조
class User:
# __init__: 객체 생성 시 자동 실행
def __init__(self, name: str, age: int, email: str):
self.name = name # 인스턴스 속성
self.age = age
self.email = email
# 메서드: 클래스에 속한 함수
def greet(self):
return f"안녕하세요, {self.name}입니다!"
def update_age(self, new_age: int):
if new_age < 0:
raise ValueError("나이는 0 이상이어야 합니다.")
self.age = new_age
def __str__(self): # print() 할 때 출력되는 문자열
return f"User(name={self.name}, age={self.age})"
# 객체 생성
user1 = User("철수", 25, "kim@example.com")
user2 = User("영희", 30, "lee@example.com")
print(user1.greet()) # 안녕하세요, 철수입니다!
print(user2.name) # 영희
user1.update_age(26)
print(user1) # User(name=철수, age=26)
클래스 vs 인스턴스
flowchart LR
CLASS["클래스 (설계도)\nclass User"] -->|"인스턴스화"| I1["객체 1\nuser1 = User('철수', 25)"]
CLASS -->|"인스턴스화"| I2["객체 2\nuser2 = User('영희', 30)"]
CLASS -->|"인스턴스화"| I3["객체 N\n..."]
클래스 변수 vs 인스턴스 변수
class Counter:
total_created = 0 # 클래스 변수: 모든 인스턴스 공유
def __init__(self, name: str):
self.name = name # 인스턴스 변수: 각 객체마다 별도
Counter.total_created += 1
@classmethod
def get_total(cls) -> int:
return cls.total_created
c1 = Counter("A")
c2 = Counter("B")
print(Counter.get_total()) # 2
상속: 기존 클래스를 확장하기
class Animal:
def __init__(self, name: str):
self.name = name
def speak(self) -> str:
return "..."
def __str__(self):
return f"{self.__class__.__name__}({self.name})"
class Dog(Animal): # Animal을 상속
def speak(self) -> str: # 메서드 오버라이드
return "멍멍!"
def fetch(self):
return f"{self.name}이(가) 공을 가져왔습니다!"
class Cat(Animal):
def speak(self) -> str:
return "야옹~"
dog = Dog("바둑이")
cat = Cat("나비")
print(dog.speak()) # 멍멍!
print(cat.speak()) # 야옹~
print(dog.fetch()) # 바둑이이(가) 공을 가져왔습니다!
print(dog) # Dog(바둑이)
실전: LLM 대화 관리 클래스
from datetime import datetime
from openai import OpenAI
class Conversation:
def __init__(self, system_prompt: str = "당신은 친절한 어시스턴트입니다."):
self.client = OpenAI()
self.messages = [{"role": "system", "content": system_prompt}]
self.created_at = datetime.now()
self.turn_count = 0
def chat(self, user_message: str) -> str:
self.messages.append({"role": "user", "content": user_message})
response = self.client.chat.completions.create(
model="gpt-4o-mini",
messages=self.messages
)
assistant_message = response.choices[0].message.content
self.messages.append({"role": "assistant", "content": assistant_message})
self.turn_count += 1
return assistant_message
def clear(self):
system = self.messages[0] # 시스템 프롬프트 유지
self.messages = [system]
self.turn_count = 0
def get_history(self) -> list:
return [m for m in self.messages if m["role"] != "system"]
def __len__(self):
return self.turn_count
def __str__(self):
return f"Conversation({self.turn_count}턴, {self.created_at.strftime('%H:%M')}시작)"
# 사용
conv = Conversation("당신은 파이썬 튜터입니다.")
print(conv.chat("리스트 컴프리헨션이 뭔가요?"))
print(conv.chat("예시 코드 보여주세요."))
print(f"현재 대화: {conv}") # Conversation(2턴, 14:30시작)
print(f"대화 수: {len(conv)}") # 2
Pydantic: 데이터 검증 클래스
LLM API 개발에서 자주 쓰이는 Pydantic을 이해하려면 클래스 개념이 필요합니다.
from pydantic import BaseModel, Field, validator
from typing import Optional, List
class UserProfile(BaseModel):
name: str
age: int = Field(ge=0, le=150) # 0 이상 150 이하
email: str
skills: List[str] = [] # 기본값 빈 리스트
bio: Optional[str] = None
@validator("email")
def validate_email(cls, v):
if "@" not in v:
raise ValueError("유효한 이메일 주소를 입력하세요.")
return v.lower()
# 유효한 데이터
user = UserProfile(name="철수", age=25, email="Kim@Example.COM")
print(user.email) # kim@example.com (자동 소문자)
print(user.skills) # []
# 잘못된 데이터
try:
bad_user = UserProfile(name="영희", age=-1, email="invalid")
except Exception as e:
print(e)
매직 메서드 (Dunder Methods)
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other): # v1 + v2
return Vector(self.x + other.x, self.y + other.y)
def __str__(self): # print(v)
return f"Vector({self.x}, {self.y})"
def __len__(self): # len(v)
return 2
def __eq__(self, other): # v1 == v2
return self.x == other.x and self.y == other.y
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # Vector(4, 6)
print(len(v1)) # 2
정리
| 개념 | 설명 |
|---|---|
class | 자료형 설계도 정의 |
__init__ | 객체 생성 시 자동 실행 초기화 메서드 |
self | 현재 인스턴스를 가리키는 참조 |
| 상속 | class Child(Parent)로 기능 확장 |
@classmethod | 인스턴스 없이 호출 가능한 메서드 |
| Pydantic | 클래스 기반 데이터 검증 라이브러리 |
다음 편에서는 외부 패키지와 가상환경 — pip로 패키지를 설치하고 프로젝트별 환경을 격리하는 방법을 배웁니다.