파이썬 상속과 다형성 이해하기 (22)
상속이란 무엇인가?
안녕하세요. 오늘은 객체지향 프로그래밍의 핵심 개념인 상속(Inheritance)과 다형성(Polymorphism)에 대해 알아보겠습니다. 먼저 상속이 무엇인지부터 살펴보겠습니다.
상속이란 무엇일까요? 상속은 객체지향 프로그래밍에서 가장 중요한 개념 중 하나입니다. 상속은 기존 클래스의 속성과 메서드를 새로운 클래스에 물려주는 것을 말합니다.
상속을 이해하기 위해 다음 예시를 살펴봅시다.
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print(f"{self.name}가 소리를 냅니다.")
class Dog(Animal):
def __init__(self, name):
super().__init__(name)
def speak(self):
print(f"{self.name}가 멍멍!")
dog = Dog("바둑이")
dog.speak() # 바둑이가 멍멍!
위 예시에서 Dog 클래스는 Animal 클래스를 상속받고 있습니다. Dog 클래스에서는 별도의 name 속성을 정의하지 않았지만, Animal 클래스로부터 name 속성을 물려받았습니다. 또한 speak() 메서드를 오버라이딩하여 개별 기능을 추가했습니다.
왜 상속이 중요할까요? 상속의 가장 큰 장점은 코드 재사용성을 높여준다는 것입니다. 기존 클래스의 속성과 메서드를 물려받아 중복 코드를 줄일 수 있습니다. 상속을 잘 활용하면 유지보수성과 확장성 높은 프로그램을 만들 수 있습니다.
또한 상속은 객체지향의 다른 핵심 원리인 캡슐화와 다형성을 구현하는 기반이 됩니다. 상위 클래스에서 공통된 속성과 메서드를 정의하고, 하위 클래스에서 이를 상속받아 개별 기능을 추가하는 방식으로 코드를 모듈화할 수 있습니다.
다형성이란 무엇인가?
다형성이란 무엇일까요? 다형성은 '여러 가지 형태를 가질 수 있는 능력'을 의미합니다. 객체지향 프로그래밍에서 다형성은 상위 클래스 타입의 참조 변수로 하위 클래스 인스턴스의 메서드를 호출할 수 있는 것을 말합니다.
다형성 역시 예시를 통해 이해해보겠습니다.
class Animal:
def speak(self):
print("동물이 소리를 냅니다.")
class Dog(Animal):
def speak(self):
print("멍멍!")
class Cat(Animal):
def speak(self):
print("야옹~")
animals = [Animal(), Dog(), Cat()]
for animal in animals:
animal.speak()
"""
출력 결과:
동물이 소리를 냅니다.
멍멍!
야옹~
"""
위 예시에서 animals 리스트에는 Animal, Dog, Cat 인스턴스가 포함되어 있습니다. 반복문을 돌면서 animal.speak()를 호출하는데, 실제로는 각 인스턴스의 오버라이딩된 speak() 메서드가 실행되는 것을 확인할 수 있습니다.
왜 다형성이 중요할까요? 다형성을 통해 상위 클래스 타입으로 서로 다른 객체를 통일된 방식으로 다룰 수 있습니다. 상위 클래스 타입의 참조 변수를 사용하면서도 실제 인스턴스에 맞는 기능을 수행하게 됩니다. 이를 통해 코드의 유연성과 재사용성, 유지보수성을 높일 수 있습니다.
다형성은 객체지향 설계의 핵심 원리인 개방-폐쇄 원칙(Open-Closed Principle)을 구현하는 기반이 됩니다. 이 원칙은 기존 코드를 수정하지 않고 새로운 기능을 추가할 수 있어야 한다는 원칙입니다. 상속과 다형성을 잘 활용하면 이 원칙을 지킬 수 있습니다.
메서드 오버라이딩
메서드 오버라이딩(Method Overriding)은 상속 관계에서 하위 클래스가 상위 클래스의 메서드를 재정의하는 것을 말합니다. 이를 통해 상위 클래스의 메서드를 오버라이딩하거나 새로운 기능을 추가할 수 있습니다.
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
print(f"{self.name}가 소리를 냅니다.")
class Dog(Animal):
def speak(self):
super().speak() # 상위 클래스 메서드 호출
print("멍멍!")
dog = Dog("바둑이")
dog.speak()
"""
출력 결과:
바둑이가 소리를 냅니다.
멍멍!
"""
위 예시에서 Dog 클래스의 speak() 메서드는 Animal 클래스의 speak() 메서드를 오버라이딩합니다. super().speak()를 통해 상위 클래스 메서드를 호출한 뒤, 추가적인 기능인 "멍멍!"을 출력합니다.
메서드 오버라이딩은 상속 계층에서 상위 클래스의 메서드를 하위 클래스에 맞게 변경하거나 확장할 수 있게 해줍니다. 다형성과 함께 유연한 코드 작성이 가능해집니다.
추상 클래스와 추상 메서드
추상 클래스(Abstract Class)와 추상 메서드(Abstract Method)는 상속과 다형성을 구현하는 데 도움이 됩니다.
추상 클래스란 무엇일까요? 추상 클래스는 인스턴스를 생성할 수 없는 클래스입니다. 추상 클래스는 주로 공통된 속성과 메서드를 정의하는 기반 클래스 역할을 합니다.
파이썬에서 추상 클래스를 만들려면 abc 모듈에서 ABC와 abstractmethod 데코레이터를 사용합니다.
from abc import ABC, abstractmethod
class AbstractAnimal(ABC):
@abstractmethod
def speak(self):
pass
class Dog(AbstractAnimal):
def speak(self):
print("멍멍!")
dog = Dog()
dog.speak() # 멍멍!
위 예시에서 AbstractAnimal 클래스는 추상 클래스입니다. @abstractmethod 데코레이터가 적용된 speak() 메서드는 추상 메서드가 됩니다. 추상 메서드는 구현부가 없는 메서드입니다.
Dog 클래스는 AbstractAnimal 클래스를 상속받으면서 speak() 메서드를 구현해야 합니다. 그렇지 않으면 에러가 발생합니다.
왜 추상 클래스와 추상 메서드가 필요할까요? 추상 클래스와 추상 메서드를 사용하면 일관된 인터페이스를 강제할 수 있습니다. 즉, 하위 클래스가 반드시 구현해야 하는 메서드를 정의할 수 있습니다. 이를 통해 코드의 통일성과 유지보수성을 높일 수 있습니다.
또한 추상 클래스와 추상 메서드는 상속 계층을 잘 설계하는 데 도움이 됩니다. 공통된 기능은 상위 클래스에 정의하고, 개별 기능은 하위 클래스에서 구현하는 방식으로 설계할 수 있습니다.
다중 상속
파이썬에서는 다중 상속(Multiple Inheritance)을 지원합니다. 다중 상속이란 하나의 클래스가 여러 개의 클래스를 상속받는 것을 말합니다.
class A:
def method_a(self):
print("A의 메서드")
class B:
def method_b(self):
print("B의 메서드")
class C(A, B):
pass
c = C()
c.method_a() # A의 메서드
c.method_b() # B의 메서드
위 예시에서 C 클래스는 A와 B 클래스를 모두 상속받습니다. 따라서 C 클래스의 인스턴스에서 A와 B의 메서드를 모두 사용할 수 있습니다.
다중 상속의 주의사항
다중 상속을 사용할 때는 주의해야 할 점이 있습니다. 상속받은 클래스에 동일한 이름의 속성이나 메서드가 있으면 메서드 해결 순서(Method Resolution Order, MRO) 에 따라 해결됩니다.
MRO는 다중 상속에서 메서드를 검색할 때 상위 클래스의 순서를 정의합니다. 기본적으로 깊이 우선 순회(depth-first) 방식을 따릅니다.
class A:
def method(self):
print("A의 메서드")
class B(A):
def method(self):
print("B의 메서드")
class C(A):
def method(self):
print("C의 메서드")
class D(B, C):
pass
d = D()
d.method() # B의 메서드
위 예제에서 D 클래스는 B와 C 클래스를 상속받습니다. B와 C 모두 A를 상속받으며, method()를 오버라이딩했습니다. 이때 d.method()를 호출하면 "B의 메서드"가 출력됩니다.
따라서 다중 상속을 사용할 때는 MRO를 인지하고 있어야 합니다. 상속 관계가 복잡해질수록 메서드 검색 순서를 파악하기 어려울 수 있습니다. 이런 경우 __mro__ 속성이나 type() 함수를 사용하면 MRO를 확인할 수 있습니다.
참고 자료
- Luciano Ramalho (2017), Fluent Python, 박완규 옮김, 인사이트. [8장 객체 참조, 뮤터블 기본값과 다중 상속]
- Allen B. Downey (2022), 파이썬을 여행하는 객체지향 탐구, 박영환 옮김, 제이펍. [파트 4: 상속과 다형성]
- Python 공식 문서, 9.7. Inheritance https://docs.python.org/3/tutorial/classes.html#inheritance
- Real Python, Python OOP - Inheritance and Polymorphism https://realpython.com/python3-object-oriented-programming/
한 고대 문서 이야기
여기 한 고대 문서가 있습니다. 이 문서는 B.C. 1,500년 부터 A.D 100년까지 약 1,600 여 년 동안 기록되었습니다. 이 문서의 저자는 약 40 명입니다. 이 문서의 고대 사본은 25,000 개가 넘으나, 사본간 오
gospel79.tistory.com
유튜브 프리미엄 월 1만원 할인받고 월 4000원에 이용하는 방법
올해 5월부터 월 8000원 정도이던 유튜브 프리미엄 요금이 15000원 정도로 인상됩니다. 각종 OTT 서비스, ChatGPT 같은 서비스들이 늘어나다보니 이런 거 몇 개만 이용하더라도 월 이용요금이 5만원을
stock79.tistory.com
"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."