본문 바로가기
Python

26. 클래스와 객체 (1) - 나만의 자료형 만들기

by 샤나엘 2026. 2. 22.
반응형

클래스와 객체 (1) - 나만의 자료형 만들기

26. 클래스와 객체 (1) - 나만의 자료형 만들기

리스트, 딕셔너리, 문자열... 지금까지 써온 것들은 모두 객체다. 이번에는 나만의 자료형을 만드는 방법, 바로 클래스(Class)를 배워보자. 객체지향 프로그래밍(OOP)의 첫걸음이다.


클래스란? - 붕어빵틀 비유

클래스를 이해하는 가장 쉬운 비유는 붕어빵틀과 붕어빵이다.

  • 클래스(Class) = 붕어빵틀 (설계도)
  • 인스턴스(Instance) = 붕어빵 (실제 만들어진 것)
  • 속성(Attribute) = 속재료 (팥, 슈크림, 피자...)
  • 메서드(Method) = 굽기, 포장하기 (할 수 있는 동작)

하나의 틀(클래스)로 여러 개의 붕어빵(인스턴스)을 만들 수 있고, 각 붕어빵은 서로 다른 속재료를 가질 수 있다.

 

클래스와 인스턴스


첫 번째 클래스 만들기

class Dog:
    pass

# 인스턴스 생성
my_dog = Dog()
your_dog = Dog()

print(type(my_dog))     # <class '__main__.Dog'>
print(my_dog == your_dog)  # False — 서로 다른 인스턴스!

class 키워드로 정의하고, 클래스 이름은 대문자로 시작한다(PascalCase).


init - 초기화 메서드

__init__은 인스턴스가 생성될 때 자동으로 호출되는 메서드다. 속성(데이터)을 초기화하는 역할을 한다.

class Dog:
    def __init__(self, name, breed, age):
        self.name = name     # 이름
        self.breed = breed   # 품종
        self.age = age       # 나이

# 인스턴스 생성
dog1 = Dog("멍멍이", "골든리트리버", 3)
dog2 = Dog("초코", "푸들", 5)

print(f"{dog1.name}은 {dog1.breed}, {dog1.age}살")
# 멍멍이은 골든리트리버, 3살

print(f"{dog2.name}은 {dog2.breed}, {dog2.age}살")
# 초코은 푸들, 5살

self란?

self인스턴스 자기 자신을 가리킨다.

class Dog:
    def __init__(self, name, age):
        self.name = name   # self.name: 인스턴스의 속성
        self.age = age     # name: 매개변수
  • self.name = name: 매개변수 name의 값을 인스턴스의 속성 self.name에 저장
  • 메서드를 호출할 때 self자동으로 전달된다. 직접 넣지 않는다.
dog1 = Dog("멍멍이", 3)
# 파이썬이 내부적으로 Dog.__init__(dog1, "멍멍이", 3) 으로 호출

메서드 (Method) - 객체의 동작

메서드는 클래스 안에 정의된 함수다. 첫 번째 매개변수는 항상 self이다.

class Dog:
    def __init__(self, name, breed, age):
        self.name = name
        self.breed = breed
        self.age = age

    def bark(self):
        print(f"{self.name}: 멍멍!")

    def info(self):
        print(f"이름: {self.name}, 품종: {self.breed}, 나이: {self.age}살")

    def birthday(self):
        self.age += 1
        print(f"{self.name}이(가) {self.age}살이 되었습니다!")

# 사용
dog = Dog("멍멍이", "골든리트리버", 3)
dog.bark()      # 멍멍이: 멍멍!
dog.info()      # 이름: 멍멍이, 품종: 골든리트리버, 나이: 3살
dog.birthday()  # 멍멍이이(가) 4살이 되었습니다!
dog.info()      # 이름: 멍멍이, 품종: 골든리트리버, 나이: 4살

클래스 변수 vs 인스턴스 변수

인스턴스 변수

self.변수명으로 정의. 각 인스턴스마다 독립적이다.

class Student:
    def __init__(self, name, score):
        self.name = name      # 인스턴스 변수
        self.score = score    # 인스턴스 변수

s1 = Student("김철수", 85)
s2 = Student("이영희", 92)

print(s1.name, s1.score)   # 김철수 85
print(s2.name, s2.score)   # 이영희 92

클래스 변수

클래스 본문에 직접 정의. 모든 인스턴스가 공유한다.

class Student:
    school = "파이썬고등학교"  # 클래스 변수 (모든 인스턴스가 공유)
    count = 0                  # 클래스 변수

    def __init__(self, name, score):
        self.name = name       # 인스턴스 변수
        self.score = score     # 인스턴스 변수
        Student.count += 1     # 클래스 변수 수정

s1 = Student("김철수", 85)
s2 = Student("이영희", 92)
s3 = Student("박민수", 78)

print(f"학교: {Student.school}")     # 파이썬고등학교
print(f"학생 수: {Student.count}")   # 3

# 인스턴스에서도 접근 가능 (읽기)
print(f"{s1.name}의 학교: {s1.school}")  # 파이썬고등학교

차이점 정리

class Example:
    class_var = "클래스 변수"        # 모든 인스턴스가 공유

    def __init__(self):
        self.instance_var = "인스턴스 변수"  # 각자 독립

a = Example()
b = Example()

# 클래스 변수 — 공유
print(a.class_var)   # 클래스 변수
print(b.class_var)   # 클래스 변수

# 인스턴스 변수 — 독립
a.instance_var = "A의 값"
print(a.instance_var)   # A의 값
print(b.instance_var)   # 인스턴스 변수 (영향 없음)

주의: 인스턴스에서 클래스 변수와 같은 이름으로 대입하면, 인스턴스 변수가 새로 생성되어 클래스 변수를 가린다.


실전 예시: 은행 계좌 클래스

class BankAccount:
    interest_rate = 0.02   # 클래스 변수: 연 이자율 2%

    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance

    def deposit(self, amount):
        """입금"""
        if amount > 0:
            self.balance += amount
            print(f"[입금] {amount:,}원 → 잔액: {self.balance:,}원")
        else:
            print("입금액은 0보다 커야 합니다.")

    def withdraw(self, amount):
        """출금"""
        if amount > self.balance:
            print(f"잔액 부족! (잔액: {self.balance:,}원)")
        elif amount <= 0:
            print("출금액은 0보다 커야 합니다.")
        else:
            self.balance -= amount
            print(f"[출금] {amount:,}원 → 잔액: {self.balance:,}원")

    def apply_interest(self):
        """이자 적용"""
        interest = int(self.balance * BankAccount.interest_rate)
        self.balance += interest
        print(f"[이자] {interest:,}원 적용 → 잔액: {self.balance:,}원")

    def info(self):
        """계좌 정보"""
        print(f"예금주: {self.owner}, 잔액: {self.balance:,}원")

# 사용
acc = BankAccount("김철수")
acc.deposit(1000000)     # [입금] 1,000,000원 → 잔액: 1,000,000원
acc.deposit(500000)      # [입금] 500,000원 → 잔액: 1,500,000원
acc.withdraw(200000)     # [출금] 200,000원 → 잔액: 1,300,000원
acc.apply_interest()     # [이자] 26,000원 적용 → 잔액: 1,326,000원
acc.info()               # 예금주: 김철수, 잔액: 1,326,000원

실전 예시: 학생 성적 관리

class Student:
    def __init__(self, name, student_id):
        self.name = name
        self.student_id = student_id
        self.scores = {}    # 과목별 점수

    def add_score(self, subject, score):
        """과목 점수 추가"""
        self.scores[subject] = score

    def get_average(self):
        """평균 계산"""
        if not self.scores:
            return 0
        return sum(self.scores.values()) / len(self.scores)

    def get_grade(self):
        """학점 산출"""
        avg = self.get_average()
        if avg >= 90: return "A"
        elif avg >= 80: return "B"
        elif avg >= 70: return "C"
        elif avg >= 60: return "D"
        else: return "F"

    def report(self):
        """성적표 출력"""
        print(f"\n{'='*30}")
        print(f"  {self.name} ({self.student_id})")
        print(f"{'='*30}")
        for subject, score in self.scores.items():
            print(f"  {subject}: {score}점")
        print(f"{'-'*30}")
        print(f"  평균: {self.get_average():.1f}점 ({self.get_grade()})")

# 사용
s = Student("김철수", "2025001")
s.add_score("국어", 85)
s.add_score("영어", 92)
s.add_score("수학", 78)
s.report()

출력:

==============================
  김철수 (2025001)
==============================
  국어: 85점
  영어: 92점
  수학: 78점
------------------------------
  평균: 85.0점 (B)

직접 해보기

문제 1. Rectangle 클래스를 만들어보자. 가로(width)와 세로(height)를 속성으로 가지고, area() (넓이)와 perimeter() (둘레) 메서드를 구현하자.

문제 2. Counter 클래스를 만들어보자. increment(), decrement(), reset(), get_value() 메서드를 구현하자. 초기값은 0이다.

문제 3. Book 클래스를 만들어보자. 제목, 저자, 가격 속성과 discount(percent) 메서드(할인 적용), info() 메서드를 구현하자. 클래스 변수로 total_books (총 책 수)를 관리하자.

문제 4. TodoList 클래스를 만들어보자. add(task), done(index), show() 메서드를 구현하자. 각 할 일은 완료 여부를 가진다.

정답 보기
# 문제 1 - Rectangle
class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
‍
    def area(self):
        return self.width * self.height
‍
    def perimeter(self):
        return 2 * (self.width + self.height)
‍
r = Rectangle(5, 3)
print(f"넓이: {r.area()}")       # 15
print(f"둘레: {r.perimeter()}")  # 16
‍
‍
# 문제 2 - Counter
class Counter:
    def __init__(self):
        self.value = 0
‍
    def increment(self):
        self.value += 1
‍
    def decrement(self):
        self.value -= 1
‍
    def reset(self):
        self.value = 0
‍
    def get_value(self):
        return self.value
‍
c = Counter()
c.increment()
c.increment()
c.increment()
c.decrement()
print(c.get_value())  # 2
c.reset()
print(c.get_value())  # 0
‍
‍
# 문제 3 - Book
class Book:
    total_books = 0
‍
    def __init__(self, title, author, price):
        self.title = title
        self.author = author
        self.price = price
        Book.total_books += 1
‍
    def discount(self, percent):
        self.price = int(self.price * (1 - percent / 100))
        print(f"할인 적용! 새 가격: {self.price:,}원")
‍
    def info(self):
        print(f"'{self.title}' by {self.author} - {self.price:,}원")
‍
b1 = Book("파이썬 입문", "김파이", 25000)
b2 = Book("자료구조", "이코딩", 30000)
b1.info()           # '파이썬 입문' by 김파이 - 25,000원
b1.discount(20)     # 할인 적용! 새 가격: 20,000원
print(f"총 책 수: {Book.total_books}")  # 2
‍
‍
# 문제 4 - TodoList
class TodoList:
    def __init__(self):
        self.tasks = []
‍
    def add(self, task):
        self.tasks.append({"task": task, "done": False})
        print(f"추가: {task}")
‍
    def done(self, index):
        if 0 <= index < len(self.tasks):
            self.tasks[index]["done"] = True
            print(f"완료: {self.tasks[index]['task']}")
        else:
            print("잘못된 번호입니다.")
‍
    def show(self):
        print("\n=== 할 일 목록 ===")
        for i, t in enumerate(self.tasks):
            status = "V" if t["done"] else " "
            print(f"  [{status}] {i}. {t['task']}")
‍
todo = TodoList()
todo.add("파이썬 공부")
todo.add("운동하기")
todo.add("장보기")
todo.done(0)
todo.show()

오늘의 정리

항목 내용
클래스 객체의 설계도. class 이름:으로 정의
인스턴스 클래스로부터 만들어진 실제 객체. 이름()으로 생성
__init__ 초기화 메서드. 인스턴스 생성 시 자동 호출
self 인스턴스 자기 자신을 가리킴. 메서드의 첫 매개변수
인스턴스 변수 self.변수 — 각 인스턴스마다 독립
클래스 변수 클래스 본문에 정의 — 모든 인스턴스가 공유

다음 편 예고: 클래스와 객체 (2) - 상속과 다형성

기존 클래스를 확장해서 새 클래스를 만드는 상속, 같은 메서드가 다르게 동작하는 다형성을 알아보자!


태그: 파이썬 Python 파이썬독학 클래스 class 객체 OOP init 인스턴스 파이썬중급 IT교육

반응형

댓글