Python 기초

Python 객체 지향 프로그래밍

OOP는 상태를 소유하고 명확한 인터페이스로 동작을 제공하는 객체 중심으로 프로그램을 구성합니다.

Python 객체 지향 프로그래밍이란?

OOP는 협력하는 객체로 시스템을 모델링합니다. 각 객체가 상태와 허용된 연산을 결합하여 큰 프로그램에서도 책임과 변경 경계를 명확하게 합니다.

OOP의 네 가지 핵심 원칙

  • 캡슐화: 상태와 동작을 함께 두고 접근을 제어합니다.
  • 추상화: 필요한 인터페이스를 제공하고 불필요한 구현을 숨깁니다.
  • 상속: 진정한 is-a 관계일 때 기존 클래스를 특수화합니다.
  • 다형성: 서로 다른 객체에 같은 연산을 사용하고 각자의 동작을 실행합니다.

예제 설계

  1. BankAccount가 잔액을 소유하고 입금을 검증합니다.
  2. SavingsAccount가 기본 계약을 재사용합니다.
  3. month_end를 오버라이드해 이자를 적용합니다.
  4. 반복문은 동일한 인터페이스로 모든 계좌를 처리합니다.
설계 팁: 객체가 다른 객체를 단순히 사용한다면 합성을 우선하고, 자식이 부모를 올바르게 대체할 때만 상속을 사용하세요.

기본 예제

코드 실행 →
main.py
class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self._balance = float(balance)

    @property
    def balance(self):
        return self._balance

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("Deposit must be positive")
        self._balance += amount

    def month_end(self):
        return "No interest"


class SavingsAccount(BankAccount):
    def __init__(self, owner, balance, interest_rate):
        super().__init__(owner, balance)
        self.interest_rate = interest_rate

    def month_end(self):
        self._balance *= 1 + self.interest_rate
        return "Interest applied"


accounts = [
    BankAccount("Ada", 100),
    SavingsAccount("Lin", 200, 0.05),
]
accounts[0].deposit(50)

for account in accounts:
    status = account.month_end()
    print(f"{account.owner}: {account.balance:.2f} ({status})")

예상 출력

Ada: 150.00 (No interest)
Lin: 210.00 (Interest applied)

OOP의 네 가지 원칙 실습

이 예제들은 Python에서 캡슐화, 추상화, 상속, 다형성을 일상적인 코드에 적용하는 방법을 보여 줍니다.

캡슐화

코드 실행 →

상태와 동작을 함께 두고 접근을 제어합니다.

encapsulation.py
class BankAccount:
    def __init__(self, balance=0):
        # Double underscores prevent accidental direct access.
        self.__balance = balance

    @property
    def balance(self):
        # Expose read access without exposing mutation.
        return self.__balance

    def deposit(self, amount):
        # Keep validation next to the state it protects.
        if amount <= 0:
            raise ValueError("Amount must be positive")
        self.__balance += amount


account = BankAccount(100)
account.deposit(50)
print(account.balance)  # 150

추상화

코드 실행 →

필요한 인터페이스를 제공하고 불필요한 구현을 숨깁니다.

abstraction.py
from abc import ABC, abstractmethod


class PaymentMethod(ABC):
    @abstractmethod
    def pay(self, amount):
        # Callers know what pay does, not how each provider does it.
        pass


class CreditCard(PaymentMethod):
    def pay(self, amount):
        # The concrete class hides its payment implementation.
        return f"Paid ${amount:.2f} by card"


payment = CreditCard()
print(payment.pay(25))

상속

코드 실행 →

진정한 is-a 관계일 때 기존 클래스를 특수화합니다.

inheritance.py
class Employee:
    def __init__(self, name):
        self.name = name

    def describe(self):
        return self.name


class Developer(Employee):
    def __init__(self, name, language):
        # Reuse initialization from the parent class.
        super().__init__(name)
        self.language = language

    def describe(self):
        # Extend inherited behavior instead of duplicating it.
        return f"{super().describe()} writes {self.language}"


print(Developer("Ada", "Python").describe())

다형성

코드 실행 →

서로 다른 객체에 같은 연산을 사용하고 각자의 동작을 실행합니다.

polymorphism.py
class EmailNotification:
    def send(self, message):
        return f"Email: {message}"


class SmsNotification:
    def send(self, message):
        return f"SMS: {message}"


def notify(channels, message):
    for channel in channels:
        # The same call selects behavior from each concrete object.
        print(channel.send(message))


channels = [EmailNotification(), SmsNotification()]
notify(channels, "Order shipped")

작동 원리

BankAccount는 잔액을 캡슐화합니다. SavingsAccount는 인터페이스를 상속하고 month_end를 오버라이드합니다. 반복문은 두 객체를 동일하게 다루며 런타임에 알맞은 메서드가 선택됩니다.

값을 변경하고 Python 설치 없이 CodeUtility 온라인 Python 컴파일러에서 실행하세요.

연습 문제

입력값과 경계 조건을 바꾸고 더 큰 데이터에서도 동작을 확인하세요.

  1. 실행 전에 출력을 예측하세요.
  2. 빈 입력과 잘못된 입력을 처리하세요.
  3. 로직을 함수로 만들고 테스트를 추가하세요.
Python IDE에서 실행 →