6.1 상속 — 정말 쉬워요
상속은 객체 지향 프로그래밍(OOP)의 기본 개념으로, 한 클래스(서브클래스 또는 자식 클래스)가 다른 클래스(슈퍼클래스 또는 부모 클래스)의 필드와 메서드를 상속받을 수 있게 해줘요.
이런 방식은 더 일반적인 클래스들을 만들고, 코드를 재사용하며, 코드의 조직화와 유지보수를 개선해줘요.
상속이 왜 필요할까요?
예를 들어, 당신이 어떤 코드를 작성해야 하고 그걸 클래스 형태로 만들려고 해요. 그런데 이미 프로젝트에 당신이 쓰고 싶은 기능을 거의 다하는 클래스가 있다고 알아냈어요. 그냥 그 클래스의 코드를 복사해 와서 쓰면 될 거예요.
하지만 "복사" 대신 상속을 사용할 수 있어요. 그 클래스를 당신의 클래스의 부모로 선언하면, Python은 부모 클래스의 기능을 당신의 클래스에 추가해 줄 거예요.
마치 자연이 개를 만들려고 하는 상황을 상상해 보세요. 박테리아부터 시작해서 개를 만드는 데 10억 년이 걸릴까요, 아니면 20만 년 만에 늑대를 길들이는 게 더 빠를까요?
기본 상속의 예
예를 들어, Animal이라는 부모 클래스가 있고, 이 클래스에는 name 필드가 있어요:
class Animal:
def __init__(self, name):
self.name = name
여기서 Dog와 Cat이라는 두 가지 서브클래스를 만들고, 둘 다 speak 메서드를 추가하려고 해요:
class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says Meow!"
위 예제에서 자식 클래스 Dog와 Cat는 Animal로부터 상속받아 speak 메서드를 추가하고 있어요.
클래스 Dog:
Animal에서name속성을 상속받아요.- 개에 특화된 문자열을 반환하는
speak메서드를 추가해요.
클래스 Cat:
Animal에서name속성을 상속받아요.- 고양이에 특화된 문자열을 반환하는
speak메서드를 추가해요.
최종 코드는 이렇게 보여요:
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says Meow!"
dog = Dog("Buddy")
cat = Cat("Whiskers")
print(dog.speak()) # 출력: Buddy says Woof!
print(cat.speak()) # 출력: Whiskers says Meow!
이 예제에서 Animal은 부모 클래스이고, Dog와 Cat은 자식 클래스예요. 자식 클래스들은 부모 클래스 Animal로부터 name 속성과 __init__ 메서드를 상속받지만, speak 메서드를 추가해요.
6.2 상속의 내부 동작
만약 부모 클래스를 추가했다면, 마치 부모 클래스의 코드를 당신의 클래스에 복사한 것과 비슷해요.
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def __init__(self, name):
super().__init__(name) # 부모 클래스의 생성자 호출
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def __init__(self, name):
super().__init__(name) # 부모 클래스의 생성자 호출
def speak(self):
return f"{self.name} says Meow!"
dog = Dog("Buddy")
cat = Cat("Whiskers")
print(dog.speak()) # 출력: Buddy says Woof!
print(cat.speak()) # 출력: Whiskers says Meow!
이 설명이 완전히 정확한 것은 아니지만, 상속을 처음 접하는 사람이라면 이렇게 생각해도 돼요. 나중에 구체적인 내용을 더 추가할 거예요.
6.3 상속의 계층 구조
복잡한 모델이나 클래스 그룹을 설계할 때, 상속의 계층 구조를 만날 수 있어요.
예를 들어, Animal이라는 클래스가 있다면, 모든 동물의 기본 클래스가 될 수 있어요:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError("Subclass must implement abstract method")
우리는 이 클래스에 speak 메서드를 추가했지만, 추상적인 동물은 말을 하지 않기 때문에 이 메서드는 NotImplementedError를 던져요. 이는 일반적인 관행이에요.
그런 다음, 동물의 카테고리에 해당하는 중간 클래스를 추가해요: Mammal은 포유류이고 Bird는 조류예요.
class Mammal(Animal):
def __init__(self, name, fur_color):
super().__init__(name) # 부모 클래스의 생성자 호출
self.fur_color = fur_color
class Bird(Animal):
def __init__(self, name, wing_span):
super().__init__(name) # 부모 클래스의 생성자 호출
self.wing_span = wing_span
def fly(self):
return f"{self.name} is flying with a wingspan of {self.wing_span} meters."
마지막으로 특정 동물 종류의 클래스가 최종적으로 나타나요:
class Dog(Mammal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Mammal):
def speak(self):
return f"{self.name} says Meow!"
class Parrot(Bird):
def speak(self):
return f"{self.name} says Squawk!"
일반적인 코드에서는 보통 이 클래스들과 함께 작업하게 돼요:
animals = [Dog("Buddy", "brown"), Cat("Whiskers", "white"), Parrot("Polly", 0.5)]
for animal in animals:
print(animal.speak())
print(f"{dog.name} has {dog.fur_color} fur.") # 출력: Buddy has brown fur.
print(f"{cat.name} has {cat.fur_color} fur.") # 출력: Whiskers has white fur.
print(parrot.fly()) # 출력: Polly is flying with a wingspan of 0.5 meters.
기술적으로는 조상들이 수십 개 있는 계층 구조를 만드는 데 제한이 없지만, 필요 없이 복잡하게 만들기보다는 단순하게 유지하는 것이 중요해요. 단순함에 힘이 있거든요.
GO TO FULL VERSION