Python 상속 완전 해설 | 단일 상속, 오버라이드, 다중 상속 활용법과 베스트 프랙티스

1. Python 상속 개요

Python에서상속은, 부모 클래스에서 자식 클래스가 기능과 속성을 물려받는 메커니즘입니다. 이를 통해, 코드 재사용성이 향상되고, 유지보수 효율성을 도모할 수 있습니다. 객체 지향 프로그래밍(OOP)의 중요한 개념 중 하나이며, 특히 대규모 시스템 개발 및 장기 프로젝트에서 유용합니다.

상속의 기본적인 역할

  • 코드 재사용성: 한 번 작성한 클래스의 기능을, 다른 클래스에서도 이용할 수 있어, 중복되는 코드를 피할 수 있습니다.
  • 유지보수의 용이성: 부모 클래스의 변경이 자식 클래스에도 자동으로 반영되기 때문에, 수정 및 기능 확장이 효율적으로 이루어집니다.
class ParentClass:
    def greet(self):
        print("안녕하세요, 부모 클래스입니다.")

class ChildClass(ParentClass):
    def greet(self):
        print("안녕하세요, 자식 클래스입니다.")
이 예에서는, ChildClassParentClass의 메서드를 덮어쓰고 있습니다. greet 메서드가 오버라이드되었기 때문에, 자식 클래스에서는 독자적인 인사가 표시됩니다.

2. Python에서의 단일 상속

단일 상속은(는) 하나의 부모 클래스에서 하나의 자식 클래스가 기능을 상속받는 형태입니다. 이것이 Python 상속의 기본 형태로, 코드의 단순성을 유지하면서도 확장성을 가질 수 있습니다.

단일 상속의 기본 구문과 예시

class Car:
    def __init__(self, brand, color):
        self.brand = brand
        self.color = color

    def describe(self):
        print(f"이 차는 {self.color}색의 {self.brand}입니다.")

class ElectricCar(Car):
    def __init__(self, brand, color, battery_size):
        super().__init__(brand, color)
        self.battery_size = battery_size

    def describe_battery(self):
        print(f"배터리 용량은 {self.battery_size}kWh입니다.")
이 예에서는 ElectricCar 클래스가 Car 클래스의 기능을 상속하면서 배터리 용량에 대한 설명 기능을 추가하고 있습니다. super()를 사용하여 부모 클래스의 생성자를 호출하고, 공통 속성(브랜드와 색상)을 초기화합니다.

3. 메서드 오버라이드

오버라이드은, 자식 클래스가 부모 클래스의 메서드를 새롭게 정의하는 기능입니다. 이를 통해, 부모 클래스의 메서드를 활용하면서도, 자식 클래스에서의 동작을 변경할 수 있습니다.

오버라이드 예시

class Animal:
    def speak(self):
        print("동물의 울음소리")

class Dog(Animal):
    def speak(self):
        print("멍멍!")
이 예에서는, Dog 클래스가 Animal 클래스의 speak 메서드를 오버라이드하고 있습니다. 이를 통해 Dog 클래스의 인스턴스에서는 「멍멍」이 출력되지만, Animal 클래스의 인스턴스에서는 「동물의 울음소리」가 표시됩니다.

4. 다중 상속

다중 상속에서는 하나의 자식 클래스가 여러 부모 클래스로부터 상속받을 수 있습니다. 이를 통해 서로 다른 클래스의 기능을 하나의 클래스에 통합할 수 있지만, 구현이 복잡해질 경우도 있으니 주의가 필요합니다.

다중 상속의 예와 주의점

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

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

class C(A, B):
    pass

c = C()
c.greet()  # "A의 인사"가 표시된다 (MRO에 따라 첫 번째 클래스가 우선한다)
Python에서는MRO(Method Resolution Order)에 의해 어떤 부모 클래스의 메서드가 호출되는지가 결정됩니다. 이 순서를 확인하려면 C.mro()를 사용합니다. 다중 상속은 강력하지만, 부모 클래스 간의 충돌이나 메서드 순서를 의식해서 사용할 필요가 있습니다.
年収訴求

5. 상속을 활용한 실전 예제

상속은 일상적인 프로그래밍에서도 유용한 경우가 많습니다. 예를 들어, 기업의 직원 관리 시스템에서 기본 직원 클래스를 상속받아 특정 직책을 가진 클래스를 만들면 코드 재사용과 확장이 가능합니다.

실전 직원 관리 시스템 예시

class Employee:
    def __init__(self, first, last, pay):
        self.first = first
        self.last = last
        self.pay = pay

    def fullname(self):
        return f'{self.first} {self.last}'

class Manager(Employee):
    def __init__(self, first, last, pay, employees=None):
        super().__init__(first, last, pay)
        self.employees = employees if employees is not None else []

    def add_employee(self, employee):
        if employee not in self.employees:
            self.employees.append(employee)

    def print_employees(self):
        for emp in self.employees:
            print(emp.fullname())
이 예제에서는 Manager 클래스가 Employee 클래스를 상속받아 직원 관리 기능을 추가합니다. 부모 클래스의 공통 기능을 유지하면서 특정 직책에 맞는 기능을 확장합니다.

6. 상속의 베스트 프랙티스와 컴포지션과의 대비

상속은 매우 강력하지만, 과도하게 사용하면 코드가 복잡해지는 위험이 있습니다. 특히, 다중 상속은 클래스 간 관계가 복잡해질 수 있기 때문에 신중히 사용해야 합니다. 이런 경우, 상속 대신컴포지션을 사용하는 것이 권장됩니다.

컴포지션 예시

컴포지션이란, 어떤 클래스가 다른 클래스를부품(인스턴스)으로 가지고 함으로써 기능을 위임하는 설계 패턴입니다.
class Engine:
    def start(self):
        print("엔진이 시작되었습니다.")

class Car:
    def __init__(self, engine):
        self.engine = engine

    def start(self):
        self.engine.start()

engine = Engine()
car = Car(engine)
car.start()  # "엔진이 시작되었습니다."가 표시됩니다
이와 같이, 상속을 사용하지 않고 기능을 클래스 간에 공유하는 방법이 컴포지션입니다. 필요한 기능만을 갖게 함으로써 코드가 보다 유연하고 관리하기 쉬워집니다.

7. 요약

Python의 상속은 코드 재사용과 확장성을 높이기 위한 강력한 도구입니다. 단일 상속, 다중 상속, 오버라이드와 같은 기술을 이해함으로써 효율적이고 유지보수가 용이한 프로그램을 만들 수 있습니다. 한편, 컴포지션과의 사용 구분을 인식하고 적절히 설계하는 것이 중요합니다. 적절한 상속을 활용하면 유연하고 견고한 코드 베이스를 구축할 수 있을 것입니다.