개발/Python

[중급반] Step 3. 객체 지향 프로그래밍(OOP) 심화: 상속과 MRO

ophelisis 2025. 12. 26. 14:29
반응형

Step 3에서는 파이썬의 객체 지향 프로그래밍을 더 깊게 파고듭니다. 단순히 클래스를 만드는 수준을 넘어, 복잡한 상속 구조에서 파이썬이 메서드를 찾는 원리인 MRO와 속성 관리를 효율적으로 도와주는 Property 기범을 마스터해 봅시다.


1. 🏗️ 상속(Inheritance)과 다중 상속

파이썬은 하나 이상의 부모 클래스로부터 기능을 물려받는 다중 상속을 지원합니다. 하지만 여러 부모가 같은 이름의 메서드를 가지고 있다면 어떤 것을 먼저 실행해야 할까요?

  • 상속: class 자식(부모): 형태로 선언하며 부모의 기능을 재사용합니다.
  • 다중 상속: 여러 클래스를 콤마로 구분하여 상속받을 수 있습니다.
Python
 
class A:
    def greet(self):
        print("A의 인사")

class B(A):
    def greet(self):
        print("B의 인사")

class C(A):
    def greet(self):
        print("C의 인사")

class D(B, C):
    pass

d = D()
d.greet() # 결과는 무엇일까요?

2. 🔍 MRO (Method Resolution Order)

위 코드에서 d.greet()을 호출하면 B의 인사가 출력됩니다. 이는 파이썬이 MRO(메서드 결정 순서) 알고리즘에 따라 탐색하기 때문입니다.

  • C3 Linearization: 파이썬이 사용하는 MRO 알고리즘 이름입니다.
  • 순서 확인: 클래스명.mro() 또는 클래스명.__mro__를 통해 확인할 수 있습니다.
  • 원칙: 자식 클래스를 먼저, 그 후 부모 클래스를 나열된 순서대로 탐색합니다.
Python
 
print(D.mro())
# [D, B, C, A, object] 순서로 탐색

3. 🛡️ Property: 데이터 캡슐화와 유효성 검사

클래스의 속성에 직접 접근하는 것은 위험할 수 있습니다. @property 데코레이터를 사용하면 속성 접근을 메서드처럼 제어할 수 있습니다.

  • Getter: 값을 읽을 때 호출 (@property)
  • Setter: 값을 수정할 때 호출 (@변수명.setter)
Python
 
class Wallet:
    def __init__(self, money):
        self.__money = money # 프라이빗 변수

    @property
    def money(self):
        return self.__money

    @money.setter
    def money(self, value):
        if value < 0:
            raise ValueError("잔액은 0원보다 작을 수 없습니다.")
        self.__money = value

my_wallet = Wallet(1000)
my_wallet.money = 500   # setter 호출
# my_wallet.money = -10 # ValueError 발생

4. ⚡ Slot: 메모리 최적화

수만 개의 객체를 생성해야 하는 상황이라면 __slots__를 사용하여 메모리 사용량을 획기적으로 줄일 수 있습니다.

  • 기능: 객체마다 생성되는 __dict__ 공간을 막고 지정된 속성만 허용합니다.
  • 장점: 속성 접근 속도가 빨라지고 메모리 점유율이 낮아집니다.
Python
 
class Point:
    __slots__ = ['x', 'y']
    def __init__(self, x, y):
        self.x = x
        self.y = y

💡 시니어의 조언: 다중 상속은 신중하게

파이썬의 MRO는 강력하지만, 너무 복잡한 다중 상속은 코드의 가독성을 떨어뜨리고 예측 불가능한 버그를 만듭니다. 가급적 믹스인(Mixin) 패턴을 활용하거나 상속 구조를 단순하게 유지하는 것이 실무에서의 핵심입니다.

반응형