클래스 - 이론 & 실습 정리

syeom·2026년 4월 8일

멀티캠퍼스 데이터분석 4월 10일 수업 내용 정리입니다.


📌 목차

이론
1. 클래스란?
2. 클래스 구성
3. 클래스 선언
4. 생성자 __init__ & self
5. 클래스 변수
6. 상속

실습

  1. 클래스 기본 실습
  2. 클래스 실습 — Calculate
  3. inplace 패턴 구현
  4. 언패킹 (*args)
  5. 상속 실습
  6. 오버라이드 (Override)
  7. 오버로딩 (Overloading)
  8. 실전 실습 — Bank & User 클래스

이론

1. 클래스란?

  • Class : 붕어빵을 찍어내는
  • Object : 붕어빵 (클래스로 만들어진 결과물)

→ 클래스는 무언가를 똑같이 만들어주는 설계도와 같은 의미입니다.


2. 클래스 구성

클래스는 크게 2가지로 구성됩니다.

구성 요소설명
속성 (Attribute)클래스가 가지는 변수
메서드 (Method)클래스가 가지는 동작(함수)

3. 클래스 선언

파이썬에서 클래스를 선언하는 방식은 class 키워드와 클래스 이름을 나열하여 선언합니다.

class 클래스이름:
    pass

💡 클래스 이름은 관례적으로 첫 글자를 대문자로 표기합니다. (PascalCase)


4. 생성자 __init__ & self

  • __init__ : 클래스를 생성 시 초기화하며 자동 실행되는 함수
  • self : 객체 자기 자신을 참조하는 매개변수
  • __init__ 함수의 첫 번째 매개변수는 반드시 self 로 선언해야 합니다.




5. 클래스 변수

  • 같은 클래스에서 생성된 객체들은 기본적으로 서로 독립적입니다.
  • 객체들끼리 정보를 공유하고 싶을 때 클래스 변수를 사용합니다.




6. 상속

클래스에서 상속이란 A 클래스가 있고 B 클래스가 있을 때, B 클래스에서 A 클래스의 기능을 물려받는 것입니다.

class B클래스(A클래스):
    pass

상속을 사용하는 이유 → 기존 클래스를 변경하지 않고 기능을 추가하거나 변경할 때 사용합니다.



실습

7. 클래스 기본 실습

class Class_1():
    def __init__(self, _a, _b):
        self.a = _a   # self(자기자신).a(공간이름) = 입력값
        self.b = _b

    def add_value(self):
        result = self.a + self.b
        return result

# 클래스 생성
a_class = Class_1(10, 5)   # __init__ 자동 호출 → self.a=10, self.b=5
b_class = Class_1(3, 7)    # __init__ 자동 호출 → self.a=3,  self.b=7

# 내부 변수 접근
print(a_class.a)            # 10
print(b_class.a)            # 3

# 내부 함수 호출
print(a_class.add_value())  # 15
print(b_class.add_value())  # 10

💡 같은 클래스로 만든 인스턴스는 같은 변수명을 써도 각각 독립된 공간에 저장됩니다.


8. 클래스 실습 — Calculate

사칙연산을 수행하는 계산기 클래스입니다.

class Calculate:
    def __init__(self, _a, _b):
        self.a = _a
        self.b = _b

    def add(self):
        return self.a + self.b

    def sub(self):
        return self.a - self.b

    def multi(self):
        return self.a * self.b

    def dev(self):
        return self.a / self.b

# 생성
cal_1 = Calculate(10, 5)
cal_2 = Calculate(3, 7)

print(cal_1.add(), cal_1.sub(), cal_1.multi(), cal_1.dev())  # 15 5 50 2.0
print(cal_2.add(), cal_2.sub(), cal_2.multi(), cal_2.dev())  # 10 -4 21 0.42...


9. inplace 패턴 구현

pandas의 inplace 옵션처럼, 원본 수정 여부를 매개변수로 제어하는 패턴입니다.

class Test2:
    def __init__(self, _a):
        self.a = _a

    def func_1(self, inplace=False):
        if inplace:
            self.a = self.a ** 2   # 원본 수정
        else:
            result = self.a ** 2
            return result          # 원본 유지, 결과만 반환

test_class2 = Test2(5)

test_class2.func_1()              # 25 반환, self.a는 5 유지
print(test_class2.a)              # 5

test_class2.func_1(inplace=True)  # self.a가 25로 변경
print(test_class2.a)              # 25

💡 print() vs return 차이

print()return
역할화면에 출력 (로그 확인용)값을 되돌려줌
저장불가능변수에 저장 가능
jupyter출력됨셀에서 확인 가능

⚠️ inplace=False를 기본값으로 쓰는 이유 → 원본 수정은 사용자가 명시적으로 선택하도록 하기 위해


10. 언패킹 (*args)

함수를 만들 때 데이터를 '바구니째' 처리할 것인지, '내용물 하나하나' 따로 처리할 것인지에 따라 *를 쓸지 결정합니다.

  • test_func(*a_list) : 리스트의 내용물만 뽑아서 각각의 변수에 전달 (언패킹)
  • test_func(a_list) : 리스트라는 바구니 자체를 전달 → TypeError 오류 발생
# *매개변수 → 여러 인자를 하나로 묶음 (패킹)
# *인자     → 묶인 데이터를 풀어서 전달 (언패킹)

1차원 리스트 언패킹

def test_func(a, b, c):
    a += 1
    b += 2
    c += 3
    return [a, b, c]

a_list = [1, 2, 3]
print(test_func(*a_list))  # [2, 4, 6]
print(a_list)              # [1, 2, 3] → 원본 유지

💡 * 언패킹은 리스트의 주소를 참조하지 않고 각 원소의 값을 복사합니다.
반면 b_list = a_list는 같은 주소를 참조하므로 한 쪽 수정 시 양쪽 모두 변경됩니다.

2차원 리스트 언패킹 주의

def test_func2(a, b, c):
    a.append(1)
    b.pop()
    c.reverse()
    return [a, b, c]

c_list = [[1, 2], [3, 4], [5, 6]]
print(test_func2(*c_list))
print(c_list)   # ⚠️ 원본도 변경됨! (내부 리스트는 참조 전달)

⚠️ 2차원 이상에서는 내부 리스트 자체의 주소가 전달되므로 원본이 변경됩니다.


11. 상속 실습

다른 클래스의 기능을 가져와 사용하는 방법입니다.

# 부모 클래스
class A:
    def __init__(self, _name, _age):
        self.name = _name
        self.age = _age

    def user_info(self):
        print(f"이름: {self.name}, 나이: {self.age}")

# 자식 클래스 — A를 상속
class B(A):
    def __init__(self, _name, _age, _addr):
        super().__init__(_name, _age)   # 부모 생성자 호출
        self.addr = _addr

    def user(self):
        print(f"이름: {self.name}, 나이: {self.age}, 주소: {self.addr}")

user_2 = B('park', 20, '서울')
user_2.user()        # 자식 함수 호출
user_2.user_info()   # 부모 함수도 사용 가능

💡 자식 클래스에서 부모 클래스의 생성자 변수를 사용하려면 super().__init__()으로 부모 생성자를 직접 호출해야 합니다.


12. 오버라이드 (Override)

부모 클래스의 메서드를 자식 클래스에서 같은 이름으로 재정의하는 방법입니다.
부모 클래스의 원본 함수는 그대로 유지됩니다.

from typing_extensions import override  # python 3.12 미만
# from typing import override           # python 3.12 이상

class Parent:
    def user_info(self):
        print("부모: 이름, 나이 출력")

class Child(Parent):
    @override
    def user_info(self):
        print("자식: 이름, 나이, 주소 출력")  # 덮어씌움

p = Parent()
c = Child()
p.user_info()   # 부모: 이름, 나이 출력
c.user_info()   # 자식: 이름, 나이, 주소 출력


13. 오버로딩 (Overloading)

파이썬은 기본적으로 오버로딩을 지원하지 않습니다.
functools.singledispatch를 사용하면 인자 타입에 따라 다른 함수를 실행할 수 있습니다.

from functools import singledispatch

@singledispatch
def show(data):
    print(f'기본형 함수: {data}')

@show.register(int)
def _(data):
    print(f'정수형 함수: {data}')

@show.register(list)
def _(data):
    print(f'리스트형 함수: {data}')

show('a')       # 기본형 함수: a
show(10)        # 정수형 함수: 10
show([1,2,3])   # 리스트형 함수: [1, 2, 3]

💡 if type(data) == int 방식으로 직접 분기해도 동일하게 구현할 수 있습니다.


14. 실전 실습 — Bank & User 클래스

Bank 클래스

class Bank:
    total_cost = 0   # 클래스 변수 — 모든 인스턴스가 공유
    user_cnt = 0     # 클래스 변수 — 생성된 계좌 수

    def __init__(self, _name, _birth, _cost=0):
        self.name = _name
        self.birth = _birth
        self.cost = _cost
        self.log = []              # ⚠️ 기본값을 []로 두면 공유 버그 발생 → 생성자 안에서 직접 생성
        Bank.total_cost += _cost
        Bank.user_cnt += 1

    def add_cost(self, _c):
        self.cost += _c
        self.log.append({'type': '입금', 'cost': _c, 'total_cost': self.cost})
        Bank.total_cost += _c
        print(f"입금 완료. 현재 잔액: {self.cost}")

    def sub_cost(self, _c):
        if self.cost >= _c:
            self.cost -= _c
            self.log.append({'type': '출금', 'cost': _c, 'total_cost': self.cost})
            Bank.total_cost -= _c
            print(f"출금 완료. 현재 잔액: {self.cost}")
            return 1   # 성공
        else:
            print('잔액이 부족합니다')
            return 0   # 실패

    def user_info(self):
        print(f'이름: {self.name}, 생년월일: {self.birth}, 잔액: {self.cost}')

⚠️ _log=[] 버그 주의
생성자 매개변수 기본값으로 []를 쓰면 모든 인스턴스가 같은 리스트를 공유합니다.
반드시 생성자 내부에서 self.log = []로 생성하세요.


User 클래스 (Bank 상속)

from typing_extensions import override

class User(Bank):
    # 클래스 변수 — 모든 유저가 공유하는 데이터
    work_type = {
        'A': 11000,
        'B': 15000,
        'C': 20000
    }
    item_list = {
        '텀블러': 50000,
        '스위치': 730000,
        '태블릿 거치대': 20000,
        '헤드셋': 520000,
        '노트북': 2000000
    }

    def __init__(self, _name, _birth, _cost):
        super().__init__(_name, _birth, _cost)
        self.items = []   # 구매한 물건 목록

    # 일하기 — 시급 * 시간 → 입금
    def work(self, _type, _time):
        if _type in User.work_type:
            amount = User.work_type[_type] * _time
            super().add_cost(amount)
        else:
            raise ValueError('저장되어 있는 일의 종류가 아닙니다.')

    # 물건 구매 — 출금 성공 시 items에 추가
    def buy_item(self, _item):
        try:
            result = super().sub_cost(User.item_list[_item])
            if result:
                self.items.append(_item)
                print('구매 성공')
            else:
                print('구매 실패')
        except Exception as e:
            print(type(e).__name__)
            print('물건의 정보가 없습니다.')

    # 오버라이드 — 부모의 user_info()를 덮어씌움
    @override
    def user_info(self):
        print(f'이름: {self.name}, 생년월일: {self.birth}, 잔액: {self.cost}, 구매 목록: {self.items}')

실행 예시

test_user = User('kim', '950101', 0)
test_user.work('C', 20)        # C타입 20시간 → 400,000원 입금
test_user.buy_item('텀블러')    # 50,000원 출금 → 구매 성공
test_user.buy_item('노트북')    # 잔액 부족 → 구매 실패
test_user.user_info()

# 부모 클래스에서 user_info() 호출 시 Bank의 함수 실행
bank1 = Bank('lee', '000101')
bank1.user_info()              # 이름, 생년월일, 잔액만 출력


📎 핵심 개념 요약

개념설명
__init__클래스 생성 시 자동 호출되는 생성자 함수
self클래스 자기 자신의 공간을 가리키는 참조
super()부모 클래스에 접근하는 방법
클래스 변수모든 인스턴스가 공유하는 변수 (Bank.total_cost)
인스턴스 변수각 인스턴스가 독립적으로 가지는 변수 (self.cost)
상속class 자식(부모) 형태로 부모 기능 재사용
오버라이드부모 메서드를 자식에서 같은 이름으로 재정의
오버로딩같은 함수를 인자 타입에 따라 다르게 동작 (singledispatch)

💡 모듈 / 라이브러리 / 프레임워크 차이

  • 모듈 : 하나의 .py 파일
  • 라이브러리 : 여러 모듈을 모아둔 폴더
  • 프레임워크 : 여러 라이브러리를 모아둔 것
profile
공부 기록

0개의 댓글