모든 강의를 무료로 볼 수 있어요. 회원가입 없이도 학습 가능합니다.

33강 / 전체 40강

메서드의 종류 — 인스턴스, 클래스, 스태틱 메서드

10분 읽기 조회 0

인스턴스·클래스·스태틱 메서드의 바인딩 원리를 이해하고 각 상황에서 올바른 메서드를 선택한다.

🎯 이 강의에서 배우는 것

파이썬의 메서드는 세 종류입니다: 인스턴스 메서드, 클래스 메서드, 스태틱 메서드. 많은 초보자가 이 세 가지를 혼동하거나 무조건 인스턴스 메서드만 씁니다. 언제 어떤 것을 선택해야 하는지 알면, 코드의 의도가 명확해지고 상속 시 올바르게 동작합니다. 세 메서드의 차이를 내부 바인딩 원리부터 이해합니다.

🔗 인스턴스 메서드 — 가장 일반적인 메서드

class Circle:
    PI = 3.14159265358979    # 클래스 변수

    def __init__(self, radius: float):
        self.radius = radius    # 인스턴스 변수

    def area(self) -> float:
        # self → 인스턴스. 인스턴스 상태와 클래스 상태 모두 접근 가능
        return self.PI * self.radius ** 2

    def scale(self, factor: float) -> None:
        # 인스턴스 상태 변경
        self.radius *= factor

    def __repr__(self) -> str:
        return f"Circle(radius={self.radius})"

c = Circle(5)
print(c.area())   # 78.539...
c.scale(2)
print(c)          # Circle(radius=10)

# 인스턴스 메서드의 바인딩 원리 — 디스크립터 프로토콜
# c.area 는 Circle.area.__get__(c, Circle) 를 호출
# → c와 area 함수를 묶은 "바운드 메서드" 반환
print(type(c.area))           # <class 'method'> (바운드)
print(type(Circle.area))      # <class 'function'> (언바운드)

# 두 호출은 완전히 동일
print(c.area())
print(Circle.area(c))    # self를 명시적으로 전달

🏭 @classmethod — 팩토리 메서드와 대안 생성자

class Date:
    def __init__(self, year: int, month: int, day: int):
        self.year = year
        self.month = month
        self.day = day

    @classmethod
    def from_string(cls, date_str: str) -> "Date":
        # cls: 클래스 자체 (인스턴스가 아님)
        # 대안 생성자 — "2024-01-15" 형식의 문자열로 생성
        year, month, day = map(int, date_str.split("-"))
        return cls(year, month, day)    # cls(...)로 인스턴스 생성

    @classmethod
    def today(cls) -> "Date":
        import datetime
        t = datetime.date.today()
        return cls(t.year, t.month, t.day)

    def __repr__(self):
        return f"Date({self.year}, {self.month}, {self.day})"

    def __str__(self):
        return f"{self.year}-{self.month:02d}-{self.day:02d}"

# 여러 방법으로 생성
d1 = Date(2024, 1, 15)
d2 = Date.from_string("2024-01-15")    # 클래스 메서드
d3 = Date.today()                       # 클래스 메서드

print(d1)   # 2024-01-15
print(d2)   # 2024-01-15
print(d1 == d2)   # False (아직 __eq__ 없음)

# 상속 시 cls의 강점: cls가 실제 클래스를 가리킴
class JapaneseDate(Date):
    def __str__(self):
        return f"{self.year}年{self.month}月{self.day}日"

jd = JapaneseDate.from_string("2024-03-15")
print(jd)           # 2024年03月15日  ← JapaneseDate 인스턴스!
print(type(jd))     # <class '__main__.JapaneseDate'>
# cls가 JapaneseDate여서 cls(year, month, day) → JapaneseDate(...)
# 만약 Date.from_string 에서 Date(...)로 하드코딩했다면 Date 인스턴스가 생겼을 것!

🔧 @staticmethod — 논리적 그루핑을 위한 유틸리티

class Date:
    def __init__(self, year: int, month: int, day: int):
        self.year = year
        self.month = month
        self.day = day

    @staticmethod
    def is_valid(year: int, month: int, day: int) -> bool:
        # 클래스나 인스턴스 상태가 전혀 필요 없음
        # self도 cls도 받지 않음
        if not (1 <= month <= 12):
            return False
        days_in_month = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
        if month == 2 and (year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)):
            return day <= 29
        return 1 <= day <= days_in_month[month]

    @classmethod
    def from_string(cls, date_str: str) -> "Date":
        y, m, d = map(int, date_str.split("-"))
        if not cls.is_valid(y, m, d):    # 클래스 메서드에서 스태틱 메서드 호출
            raise ValueError(f"유효하지 않은 날짜: {date_str}")
        return cls(y, m, d)

# 스태틱 메서드는 클래스·인스턴스 모두에서 호출 가능
print(Date.is_valid(2024, 2, 29))    # True (윤년)
print(Date.is_valid(2023, 2, 29))    # False

d = Date(2024, 1, 15)
print(d.is_valid(2024, 6, 31))       # False

# 스태틱 메서드 vs 모듈 함수의 차이
# → 스태틱은 클래스 네임스페이스 안에 두어 "이 함수는 이 클래스와 관련 있음"을 표현
# → 모듈 수준 함수로 빼도 기술적으로 동일하지만 응집도가 낮아짐

📊 세 메서드 비교표와 선택 기준

종류선언첫 번째 인자인스턴스 상태클래스 상태주요 용도
인스턴스 메서드def m(self)인스턴스✅✅ (self.__class__)일반 동작, 상태 변경
클래스 메서드@classmethod def m(cls)클래스❌✅팩토리, 대안 생성자, 서브클래스 인식
스태틱 메서드@staticmethod def m()없음❌❌유틸리티, 논리적 그루핑
# 선택 기준 예시
class BankAccount:
    _interest_rate = 0.03   # 클래스 변수

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

    # 인스턴스 메서드: 인스턴스 상태(self._balance) 필요
    def deposit(self, amount):
        self._balance += amount

    def get_balance(self):
        return self._balance

    # 클래스 메서드: 클래스 상태(_interest_rate) 변경, 상속 시 cls 중요
    @classmethod
    def set_interest_rate(cls, rate: float):
        cls._interest_rate = rate

    @classmethod
    def from_dict(cls, data: dict) -> "BankAccount":
        return cls(data["owner"], data.get("balance", 0))

    # 스태틱 메서드: 클래스/인스턴스 상태 불필요 — 순수 계산
    @staticmethod
    def format_money(amount: float) -> str:
        return f"{amount:,.0f}원"

acc = BankAccount("홍길동", 100000)
acc.deposit(50000)
print(BankAccount.format_money(acc.get_balance()))   # 150,000원

BankAccount.set_interest_rate(0.05)
acc2 = BankAccount.from_dict({"owner": "이순신", "balance": 200000})
print(acc2.owner, acc2._balance)

⚠️ 자주 하는 실수

  • @classmethod에서 self 사용: @classmethod def m(self, ...)라고 쓰면 파이썬은 오류를 내지 않지만, 실제로 self에는 클래스가 전달됩니다. 관례적으로 cls를 사용하고 인스턴스 상태가 필요하면 클래스 메서드가 아닌 인스턴스 메서드를 씁니다.
  • 팩토리 메서드에서 cls 대신 클래스 이름 하드코딩: return Date(y, m, d)로 하드코딩하면 서브클래스에서 호출해도 항상 Date가 반환됩니다. 반드시 return cls(y, m, d)를 사용하세요.
  • 인스턴스·클래스 모두 불필요한데 인스턴스 메서드 사용: self나 cls를 전혀 사용하지 않는 인스턴스 메서드는 @staticmethod로 바꾸면 의도가 명확해집니다.

📝 정리 및 다음 강의 예고

  • 인스턴스 메서드: self로 인스턴스 상태 접근. 가장 일반적.
  • @classmethod: cls로 클래스 상태 접근. 팩토리 패턴, 상속 시 서브클래스 인식.
  • @staticmethod: self/cls 없음. 클래스 네임스페이스 안 유틸리티.
  • 선택 기준: 인스턴스 상태 → 인스턴스 메서드 / 클래스 상태나 서브클래스 인식 → 클래스 메서드 / 둘 다 불필요 → 스태틱 메서드.

다음 강의: 상속과 다형성 — 코드를 재사용하고 확장하는 OOP의 핵심. super()의 내부, MRO(메서드 해석 순서), 다형성을 활용한 설계를 배웁니다.

관련 주제

  • 인스턴스 메서드
  • @classmethod
  • @staticmethod
  • 디스크립터 프로토콜
  • 팩토리 메서드 패턴
  • 바운드 메서드
  • 개발·프로그래밍
  • 개발·프로그래밍 강의
  • 파이썬 기초 40강 — 처음 배우는 프로그래밍
  • 무료강의
  • 무료 온라인 강의
  • NUGUNA
  • 누구나

📚 시리즈 전체 공유

파이썬 기초 40강 — 처음 배우는 프로그래밍

이 강의가 속한 시리즈는 총 40강, 모두 무료입니다. 처음부터 배우려는 동료에게 시리즈 전체를 알려 주세요.

댓글

0/1000

불러오는 중...