클래스 메서드와 정적 메서드

2025. 3. 26. 04:10Programming Languages/Python

클래스 메서드와 정적 메서드

1. 클래스 메서드

클래스 메서드는 클래스에 바인딩된 메서드로, @classmethod 데코레이터를 사용하여 정의합니다. 첫 번째 매개변수로 클래스 자체를 받습니다(보통 cls로 명명).

class Person:
    count = 0  # 클래스 변수
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
        Person.count += 1
    
    @classmethod
    def create_anonymous(cls, age):
        # 클래스 메서드는 클래스를 첫 번째 인자로 받음
        return cls("Anonymous", age)
    
    @classmethod
    def get_count(cls):
        return cls.count

# 인스턴스 생성
p1 = Person("홍길동", 30)
p2 = Person("김철수", 25)

# 클래스 메서드 호출
p3 = Person.create_anonymous(40)  # 익명 사용자 생성
print(p3.name, p3.age)            # 출력: Anonymous 40

# 클래스 메서드로 클래스 변수 접근
print(Person.get_count())         # 출력: 3

2. 정적 메서드

정적 메서드는 인스턴스나 클래스에 바인딩되지 않는 메서드로, @staticmethod 데코레이터를 사용하여 정의합니다. 특별한 첫 번째 매개변수가 없습니다.

class MathUtils:
    @staticmethod
    def add(x, y):
        return x + y
    
    @staticmethod
    def factorial(n):
        if n <= 1:
            return 1
        return n * MathUtils.factorial(n - 1)
    
    @staticmethod
    def is_prime(n):
        if n <= 1:
            return False
        for i in range(2, int(n**0.5) + 1):
            if n % i == 0:
                return False
        return True

# 정적 메서드 호출 (인스턴스 생성 없이도 사용 가능)
print(MathUtils.add(5, 3))       # 출력: 8
print(MathUtils.factorial(5))    # 출력: 120
print(MathUtils.is_prime(17))    # 출력: True

 

실용적인 예제: 간단한 은행 계좌 관리 시스템

아래는 지금까지 배운 객체지향 개념들을 활용한 간단한 은행 계좌 관리 시스템입니다.

from abc import ABC, abstractmethod
from datetime import datetime

# 계좌 인터페이스
class Account(ABC):
    @abstractmethod
    def deposit(self, amount):
        pass
    
    @abstractmethod
    def withdraw(self, amount):
        pass
    
    @abstractmethod
    def get_balance(self):
        pass

# 거래 내역 클래스
class Transaction:
    def __init__(self, amount, transaction_type, timestamp=None):
        self.amount = amount
        self.transaction_type = transaction_type  # "deposit" 또는 "withdraw"
        self.timestamp = timestamp or datetime.now()
    
    def __str__(self):
        return f"[{self.timestamp.strftime('%Y-%m-%d %H:%M:%S')}] {self.transaction_type}: {self.amount:,}원"

# 기본 계좌 클래스
class BankAccount(Account):
    interest_rate = 0.02  # 기본 이자율 (클래스 변수)
    
    def __init__(self, account_number, owner_name, balance=0):
        self._account_number = account_number
        self._owner_name = owner_name
        self._balance = balance
        self._transactions = []
    
    @property
    def account_number(self):
        # 계좌번호는 마지막 4자리만 공개
        hidden = '*' * (len(self._account_number) - 4)
        return f"{hidden}{self._account_number[-4:]}"
    
    @property
    def owner_name(self):
        return self._owner_name
    
    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("입금액은 0보다 커야 합니다.")
        
        self._balance += amount
        transaction = Transaction(amount, "deposit")
        self._transactions.append(transaction)
        return True
    
    def withdraw(self, amount):
        if amount <= 0:
            raise ValueError("출금액은 0보다 커야 합니다.")
        if amount > self._balance:
            raise ValueError("잔액이 부족합니다.")
        
        self._balance -= amount
        transaction = Transaction(amount, "withdraw")
        self._transactions.append(transaction)
        return True
    
    def get_balance(self):
        return self._balance
    
    def get_transaction_history(self):
        return self._transactions
    
    def __str__(self):
        return f"계좌번호: {self.account_number}, 소유자: {self.owner_name}, 잔액: {self._balance:,}원"
    
    @classmethod
    def set_interest_rate(cls, rate):
        if rate < 0:
            raise ValueError("이자율은 0보다 작을 수 없습니다.")
        cls.interest_rate = rate
    
    def apply_interest(self):
        interest = self._balance * self.interest_rate
        self.deposit(interest)
        return interest

# 당좌예금 계좌 (마이너스 통장)
class CheckingAccount(BankAccount):
    def __init__(self, account_number, owner_name, balance=0, overdraft_limit=100000):
        super().__init__(account_number, owner_name, balance)
        self._overdraft_limit = overdraft_limit
    
    def withdraw(self, amount):
        if amount <= 0:
            raise ValueError("출금액은 0보다 커야 합니다.")
        
        # 잔액 + 마이너스 한도 이내에서 출금 가능
        if amount > self._balance + self._overdraft_limit:
            raise ValueError(f"출금 한도 초과. 최대 {self._balance + self._overdraft_limit:,}원까지 출금 가능합니다.")
        
        self._balance -= amount
        transaction = Transaction(amount, "withdraw")
        self._transactions.append(transaction)
        return True
    
    def __str__(self):
        return f"{super().__str__()}, 마이너스 한도: {self._overdraft_limit:,}원"

# 적금 계좌
class SavingsAccount(BankAccount):
    interest_rate = 0.05  # 적금 계좌는 이자율이 더 높음
    
    def __init__(self, account_number, owner_name, balance=0, term_months=12):
        super().__init__(account_number, owner_name, balance)
        self._term_months = term_months
        self._creation_date = datetime.now()
    
    def withdraw(self, amount):
        # 만기일 이전에는 출금 불가
        maturity_date = self._creation_date.replace(month=self._creation_date.month + self._term_months)
        if datetime.now() < maturity_date:
            months_left = (maturity_date.year - datetime.now().year) * 12 + maturity_date.month - datetime.now().month
            raise ValueError(f"만기까지 {months_left}개월 남았습니다. 만기 전에는 출금할 수 없습니다.")
        
        return super().withdraw(amount)
    
    def __str__(self):
        maturity_date = self._creation_date.replace(month=self._creation_date.month + self._term_months)
        return f"{super().__str__()}, 만기일: {maturity_date.strftime('%Y-%m-%d')}"

# 은행 클래스
class Bank:
    def __init__(self, name):
        self.name = name
        self._accounts = {}
    
    def create_account(self, account_type, owner_name, initial_deposit=0, **kwargs):
        # 계좌번호 생성
        import random
        account_number = str(random.randint(1000000000, 9999999999))
        
        # 계좌 타입에 따라 다른 계좌 생성
        if account_type.lower() == "checking":
            overdraft_limit = kwargs.get("overdraft_limit", 100000)
            account = CheckingAccount(account_number, owner_name, initial_deposit, overdraft_limit)
        elif account_type.lower() == "savings":
            term_months = kwargs.get("term_months", 12)
            account = SavingsAccount(account_number, owner_name, initial_deposit, term_months)
        else:
            account = BankAccount(account_number, owner_name, initial_deposit)
        
        self._accounts[account_number] = account
        return account
    
    def get_account(self, account_number):
        return self._accounts.get(account_number)
    
    def transfer(self, from_account_number, to_account_number, amount):
        from_account = self.get_account(from_account_number)
        to_account = self.get_account(to_account_number)
        
        if not from_account or not to_account:
            raise ValueError("계좌번호가 잘못되었습니다.")
        
        # 출금 후 입금
        from_account.withdraw(amount)
        to_account.deposit(amount)
        
        return True

# 사용 예제
if __name__ == "__main__":
    # 은행 생성
    bank = Bank("파이썬 은행")
    
    # 계좌 생성
    checking = bank.create_account("checking", "홍길동", 1000000, overdraft_limit=500000)
    savings = bank.create_account("savings", "홍길동", 5000000, term_months=24)
    
    # 계좌 정보 출력
    print(checking)
    print(savings)
    
    # 거래 수행
    checking.deposit(500000)
    try:
        checking.withdraw(2000000)  # 잔액 초과지만 마이너스 한도 내
    except ValueError as e:
        print(f"오류: {e}")
    
    try:
        savings.withdraw(1000000)  # 만기 전 출금 시도
    except ValueError as e:
        print(f"오류: {e}")
    
    # 계좌 이체
    try:
        bank.transfer(checking._account_number, savings._account_number, 300000)
    except ValueError as e:
        print(f"오류: {e}")
    
    # 거래 내역 확인
    print("\n거래 내역:")
    for transaction in checking.get_transaction_history():
        print(transaction)
    
    # 이자 적용
    interest = checking.apply_interest()
    print(f"\n적용된 이자: {interest:,}원")
    print(f"이자 적용 후 잔액: {checking.get_balance():,}원")

 

'Programming Languages > Python' 카테고리의 다른 글

연결 리스트(Linked List)  (0) 2025.03.27
특수 메서드 (매직 메서드)  (0) 2025.03.26
추상 클래스와 인터페이스  (0) 2025.03.26
다형성 (Polymorphism)  (0) 2025.03.26
캡슐화(Encapsulation)  (0) 2025.03.26