상세 컨텐츠

본문 제목

객체지향 프로그래밍 4대 패러다임

인공지능_2026/1. 파이썬

by Ryuzy 2026. 4. 28. 17:14

본문

반응형

객체지향 프로그래밍(OOP) 4대 패러다임은 캡슐화, 상속, 다형성, 추상화입니다. 캡슐화는 데이터(속성)와 기능(메서드)을 하나로 묶고 외부에서 직접 접근을 제한하여 객체를 보호하는 개념이며, 상속은 기존 클래스의 속성과 메서드를 물려받아 재사용하고 확장하는 구조를 의미합니다. 다형성은 같은 이름의 메서드라도 객체의 종류에 따라 다르게 동작할 수 있게 하는 특징으로, 코드의 유연성을 높여줍니다. 추상화는 복잡한 내부 구현은 숨기고 핵심적인 기능만 외부에 제공하는 것으로, 사용자는 세부 동작을 몰라도 객체를 쉽게 사용할 수 있게 해줍니다. 이 네 가지 개념을 통해 코드의 재사용성, 확장성, 유지보수성을 크게 향상시킬 수 있습니다.

 

1. 캡슐화

캡슐화(encapsulation)는 객체지향 프로그래밍에서 데이터(속성)와 이를 처리하는 메서드를 하나로 묶고, 외부에서 내부 구현에 직접 접근하지 못하도록 제한하여 객체를 보호하는 개념입니다. 파이썬에서는 public, _protected, __private와 같은 네이밍 규칙을 통해 접근 수준을 구분하며, 필요할 경우 getter/setter 메서드나 @property를 사용해 안전하게 값을 읽고 수정할 수 있습니다. 이를 통해 객체의 상태를 임의로 변경하는 것을 방지하고, 코드의 안정성과 유지보수성을 높일 수 있습니다.

class Fruit:
    def __init__(self, name, price):
        self.name = name          # public
        self.__price = price      # private (캡슐화)

    # getter
    def get_price(self):
        return self.__price

    def set_price(self, price):
        if price > 0:
            self.__price = price
        else:
            print("가격은 0보다 커야 합니다.")

    def print_info(self):
        print(f"과일: {self.name}")
        print(f"가격: {self.__price}")


class Apple(Fruit):
    def __init__(self, name, price, origin):
        super().__init__(name, price)
        self.origin = origin

    def print_origin(self):
        print(f"{self.name}의 원산지: {self.origin}")


apple = Apple("사과", 3000, "한국")

apple.print_info()
apple.print_origin()

print("가격 조회:", apple.get_price())

print("----- 가격 수정 -----")
apple.set_price(3500)
print("수정된 가격:", apple.get_price())

print("----- 잘못된 값 입력 -----")
apple.set_price(-1000)  # 검증 실패

print("----- 외부 접근 시도 -----")
apple.__price = 10000   # 새로운 변수 생성 (실제 private 변경 아님)

print("getter로 확인:", apple.get_price())  # 여전히 3500
print("직접 접근:", apple.__price)         # 10000 (다른 변수)

👉 self.__price 는 실제로 private가 되는 것이 아니라 내부적으로 _Fruit__price 으로 바뀝니다.

 

class Fruit:
    def __init__(self, name, price):
        self.name = name
        self.__price = price  # private

    # getter → 속성처럼 접근 가능
    @property
    def price(self):
        return self.__price

    # setter → 값 할당 시 자동 호출
    @price.setter
    def price(self, value):
        if value > 0:
            self.__price = value
        else:
            print("가격은 0보다 커야 합니다.")

    def print_info(self):
        print(f"과일: {self.name}")
        print(f"가격: {self.price}")  # 함수가 아니라 속성처럼 사용


class Apple(Fruit):
    def __init__(self, name, price, origin):
        super().__init__(name, price)
        self.origin = origin

    def print_origin(self):
        print(f"{self.name}의 원산지: {self.origin}")


apple = Apple("사과", 3000, "한국")

apple.print_info()
apple.print_origin()

print("가격 조회:", apple.price)  # getter 호출

print("----- 가격 수정 -----")
apple.price = 3500               # setter 호출
print("수정된 가격:", apple.price)

print("----- 잘못된 값 입력 -----")
apple.price = -1000              # 검증 실패

print("----- 외부 접근 시도 -----")
apple.__price = 10000            # 새로운 변수 생성

print("실제 가격:", apple.price)  # 여전히 3500
print("외부에서 만든 값:", apple.__price)

👉 @property는 getter/setter를 함수가 아닌 “속성처럼” 사용하게 해주는 Pythonic한 캡슐화 방식입니다.

 

 

2. 상속

상속(inheritance)은 객체지향 프로그래밍에서 기존 클래스(부모 클래스)의 속성과 메서드를 새로운 클래스(자식 클래스)가 물려받아 재사용하고 확장하는 개념입니다. 이를 통해 공통 기능을 중복 작성하지 않고 코드 재사용성을 높일 수 있으며, 자식 클래스는 부모의 기능을 그대로 사용하거나 필요한 부분만 추가하거나 오버라이딩(재정의)하여 동작을 변경할 수 있습니다. 파이썬에서는 class 자식클래스(부모클래스) 형태로 상속을 구현하며, super()를 사용해 부모 클래스의 초기화나 메서드를 호출할 수 있습니다. 이러한 구조는 코드의 유지보수성과 확장성을 크게 향상시키는 핵심 개념입니다.

class Parent:
    pass

class Child(Parent):
    pass

 

1. 상속 구조

class Fruit:
    def __init__(self, name, quantity):
        self.name = name
        self.quantity = quantity

    def store(self, place):
        print(f'{self.name}를 {place}에 보관합니다')

    def sell(self, amount):
        print(f'{self.name}를 {amount}개 판매합니다')

fruit = Fruit('과일', 100)
fruit.store('창고')
fruit.sell(10)

class Apple(Fruit):
    pass

# Fruit 클래스를 상속 받았기 때문에
# Fruit 생성자의 매개변수를 전달해야 함
apple = Apple('사과', 50)

apple.store('냉장고')
apple.sell(5)

 

 2. 생성자 호출

👉 자식 클래스에 __init__이 없을 때 자동 호출

class Fruit:
    def __init__(self, name):
        print("Fruit 생성자 호출")
        self.name = name

class Apple(Fruit):
    pass

apple = Apple("사과")

 

👉 자식 클래스에 __init__을 직접 정의하면 부모 생성자 호출 안됨

class Fruit:
    def __init__(self, name):
        print("Fruit 생성자 호출")
        self.name = name

class Apple(Fruit):
    def __init__(self, name):
        print("Apple 생성자 호출")
        # self.name = name

apple = Apple("사과")
print(apple.name) # AttributeError: 'Apple' object has no attribute 'name'

 

👉  super() 함수는 현재 클래스의 부모 클래스를 참조하며, 부모 클래스의 생성자를 호출할 수 있습니다.

class Apple(Fruit):
    def __init__(self, name):
        super().__init__(name)  # 부모 생성자 호출
        print("Apple 생성자 호출")

 

 3. 오버라이딩

오버라이딩(overriding)은 상속 관계에서 부모 클래스에 이미 정의된 메서드를 자식 클래스에서 동일한 이름으로 다시 정의하여, 동작을 변경하는 것을 의미합니다. 이를 통해 자식 클래스는 부모의 기본 기능을 그대로 사용할 수도 있고, 필요에 따라 일부 또는 전체 동작을 자신에게 맞게 수정할 수 있습니다. 파이썬에서는 별도의 키워드 없이 같은 메서드 이름을 정의하면 자동으로 오버라이딩이 이루어지며, 필요할 경우 super()를 사용해 부모의 메서드를 함께 호출할 수도 있습니다.

class Fruit:
    def __init__(self, name, quantity):
        self.name = name
        self.quantity = quantity

    def eat(self):
        print(f'{self.name}를 먹습니다')

    def store(self, place):
        print(f'{self.name}를 {place}에 보관합니다')

class Apple(Fruit):
    def wash(self):
        print(f'{self.name}를 깨끗이 씻습니다')

    # 오버라이딩
    def eat(self):
        print(f'{self.name}를 아주 맛있게 먹습니다')

    # 부모 메서드 호출
    def superEat(self):
        super().eat()

apple = Apple('사과', 10)

apple.eat()        # 오버라이딩된 메서드
apple.store('냉장고')  # 부모 메서드
apple.wash()       # 자식 메서드

print("----- 부모 메서드 호출 -----")
apple.superEat()   # 부모 eat 호출

fruit = Fruit('과일', 20)
fruit.eat()
fruit.store('창고')
# fruit.wash()  (Fruit에는 없음)

 

 4. 다중 상속

다중 상속은 클래스가  이상의 부모 클래스로부터 상속을 받는 기능을 의미합니다. 파이썬은 다른 많은 객체 지향 언어와 달리 다중 상속을 지원합니다. 다중 상속을 사용하면 코드의 재사용성을 향상시킬  있지만, 동시에 복잡성이 높아지기 때문에 주의해야 합니다.

class Parent1:
    pass

class Parent2:
    pass

class Child(Parent1, Parent2):
    pass

 

class Fruit:
    def __init__(self, name, quantity):
        self.name = name
        self.quantity = quantity

    def eat(self):
        print(f'{self.name}를 먹습니다')

    def sleep(self, hour):
        print(f'{self.name}를 {hour}시간 동안 보관합니다')


class Storage:
    def __init__(self, name, quantity):
        self.name = name
        self.quantity = quantity

    def process(self, hour):
        print(f'{self.name}를 {hour}시간 동안 가공합니다')

    def sleep(self, hour):
        print(f'{self.name}를 {hour}시간 동안 저온 숙성합니다')

# 다중 상속
class ProcessedFruit(Fruit, Storage):
    pass

fruit = ProcessedFruit('사과', 10)

fruit.eat()       # Fruit
fruit.process(2)  # Storage
fruit.sleep(8)    # MRO에 따라 Fruit의 sleep 실행

print(ProcessedFruit.mro())

 

object 클래스

파이썬의 object 클래스는 모든 클래스의 최상위 부모 클래스(루트 클래스)로, 사용자가 클래스를 정의할 때 명시적으로 상속하지 않아도 자동으로 상속되는 기본 클래스입니다. 이 클래스는 객체가 가져야 할 최소한의 공통 기능을 제공하며, __str__, __repr__, __eq__ 같은 기본 매직 메서드의 기본 구현을 포함하고 있습니다. 따라서 모든 파이썬 객체는 내부적으로 object를 기반으로 동작하며, 새로운 클래스를 정의할 때도 이 기본 기능들을 물려받아 사용할 수 있습니다. 즉, object 클래스는 파이썬 객체 시스템의 가장 기초가 되는 공통 부모입니다.

 

MRO

MRO(Method Resolution Order)는 파이썬에서 다중 상속을 사용할 때, 메서드나 속성을 찾는 순서를 정의하는 규칙입니다. MRO는 특히 여러 부모 클래스를 상속받는 경우에 어떤 부모 클래스에서 메서드를 먼저 찾을지를 결정하며, 이를 통해 클래스 간의 메서드 충돌을 해결할 수 있습니다.

class Base:
    def hello(self):
        print("Base")
        
class Clean(Base):
    def hello(self):
        print("Clean")
        super().hello()

class Pack(Base):
    def hello(self):
        print("Pack")
        super().hello()

class Product(Clean, Pack):
    def hello(self):
        print("Product")
        super().hello()

p = Product()
p.hello()

print(Product.mro())

 

 

3. 다형성

다형성(polymorphism)은 같은 이름의 메서드나 함수를 호출하더라도, 객체의 종류에 따라 서로 다른 방식으로 동작하는 특성을 의미합니다. 파이썬은 정적 타입 언어처럼 명시적인 인터페이스 구현을 요구하지 않고, 동일한 메서드를 가지고 있으면 타입과 관계없이 사용할 수 있는 덕 타이핑(duck typing)을 기반으로 다형성을 자연스럽게 지원합니다. 예를 들어 여러 객체가 pay()나 speak() 같은 동일한 메서드를 가지고 있으면, 하나의 함수에서 객체의 타입을 구분하지 않고도 공통된 방식으로 처리할 수 있습니다. 이러한 특징은 코드의 유연성과 확장성을 높여주며, 새로운 기능을 추가할 때 기존 코드를 수정하지 않아도 되는 장점을 제공합니다.

class CardPayment:
    def pay(self, amount):
        print(f"카드로 {amount}원 결제합니다")

class CashPayment:
    def pay(self, amount):
        print(f"현금으로 {amount}원 결제합니다")

class KakaoPay:
    def pay(self, amount):
        print(f"카카오페이로 {amount}원 결제합니다")

def process_payment(payment, amount):
    payment.pay(amount)

card = CardPayment()
cash = CashPayment()
kakao = KakaoPay()

process_payment(card, 10000)
process_payment(cash, 5000)
process_payment(kakao, 2000)

 

 

4. 추상화

추상화(abstraction)는 복잡한 내부 구현은 숨기고, 외부에서는 필요한 핵심 기능만 간단하게 사용할 수 있도록 만드는 개념입니다. 사용자는 객체가 내부적으로 어떻게 동작하는지 몰라도, 제공된 메서드만으로 기능을 사용할 수 있으며, 이를 통해 코드의 복잡도를 줄이고 이해와 사용을 쉽게 합니다. 파이썬에서는 일반 클래스 설계뿐 아니라 abc 모듈의 ABC와 @abstractmethod를 사용해 추상 클래스를 정의하고, 반드시 구현해야 하는 메서드를 강제함으로써 일관된 구조를 유지할 수 있습니다. 즉, 추상화는 “어떻게 동작하는지”보다 “무엇을 할 수 있는지”에 집중하게 만드는 설계 방식입니다.

from abc import ABC, abstractmethod

# 추상 클래스
class Payment(ABC):
    
    @abstractmethod
    def pay(self, amount):
        pass  # 반드시 구현해야 하는 메서드

# 카드 결제
class CardPayment(Payment):
    def pay(self, amount):
        print(f"카드로 {amount}원 결제합니다")

# 현금 결제
class CashPayment(Payment):
    def pay(self, amount):
        print(f"현금으로 {amount}원 결제합니다")

# 카카오페이
class KakaoPay(Payment):
    def pay(self, amount):
        print(f"카카오페이로 {amount}원 결제합니다")

# 공통 처리 함수
def process_payment(payment: Payment, amount):
    payment.pay(amount)

# 사용
card = CardPayment()
cash = CashPayment()
kakao = KakaoPay()

process_payment(card, 10000)
process_payment(cash, 5000)
process_payment(kakao, 2000)

 

 

반응형

'인공지능_2026 > 1. 파이썬' 카테고리의 다른 글

매직 메서드  (1) 2026.04.30
예외 처리  (0) 2026.04.30
객체지향 프로그래밍(OOP)  (0) 2026.04.27
사용자 정의 함수  (0) 2026.04.27
제어문 - 반복문  (0) 2026.04.17

관련글 더보기