클래스의 상속
여러 언어들과 같이 클래스가 있다면 상속이란 개념이 있다.
상속(inheritance)은 부모 클래스(상위 클래스)의 기능을 자식 클래스(하위 클래스)가 물려받는 것이다.
상속에서의 부모 클래스의 경우는 공통되는 기능을 정의하고, 자식 클래스는 부모의 기능을 그대로 쓰거나 필요에 따라서 변경하는 오버라이딩을 해서 사용한다.
상속의 기본 문법
파이썬에서 상속은 우선 두개의 클래스를 정의하고 자식이 되는 클래스 명 옆에 전달인자로 부모 클래스명을 전달하면 된다.
class 부모클래스:
# 부모 클래스의 내용
...
class 자식클래스(부모클래스):
# 부모 클래스의 내용을 상속받음
...
기본적인 상속의 예시를 보자면
# 부모 클래스
class Animal:
def speak(self):
print("소리를 낸다.")
# 자식 클래스
class Dog(Animal):
def bark(self):
print("멍멍!")
# 객체 생성
d = Dog()
# 부모 메서드 사용
d.speak() # 소리를 낸다.
# 자식 메서드 사용
d.bark() # 멍멍!
이렇게 사용할 수 있다.
보면 상속한 자식 클래스로 생성한 객체에서 부모 클래스의 기능인 speak를 사용할 수 있는 것을 볼 수 있다.
오버라이딩
자식 클래스에서는 부모클래스에서 받은 함수를 그대로 사용하는 것도 가능하지만 가져와서 원하는 대로 변경해서 사용도 가능하다.
class Animal:
def speak(self):
print("소리를 낸다.")
class Dog(Animal):
def speak(self):
print("멍멍!")
d = Dog()
d.speak() # 멍멍!
보면 부모 클래스에서 speak()는 "소리를 낸다." 라는 내용을 출력하나 자식 클래스에서 동일한 함수를 내용을 바꿔 선언하면 자식 클래스에서는 다른 기능을 함을 볼 수 있다.
super() 함수
super() 함수는 부모 클래스의 메서드를 자식 클래스의 내부에서 호출하고 싶을때 사용한다.
class Animal:
def speak(self):
print("소리를 낸다.")
class Dog(Animal):
def speak(self):
super().speak() # 부모 메서드 호출
print("멍멍!")
d = Dog()
d.speak()
================================================
# 소리를 낸다.
# 멍멍!
보면 speak()를 call할때 부모의 speak를 먼저 호출하고 자식의 speak의 내부의 print문이 출력되는 것을 볼 수 있다.
생성자 상속
부모의 __init__()도 상속받을 수 있으며 필요하면 super().__init__()으로 부모의 초기화도 같이 해줄 수 있게 된다.
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # 부모 초기화
self.breed = breed
d = Dog("초코", "푸들")
print(d.name) # 초코
print(d.breed) # 푸들
추가로 자동차를 예시로 들어보자.
자동차라는 기본적인 개념으로써 클래스가 하나 존재한다고 하자.
그리고 이 자동자가 가지는 기본적인 멤버변수로 모델명, 바퀴의 색, 몸통의 색, 마력이란 정보를 가지고 있다고 하면 아래와 같이 클래스를 정의할 수 있다.
class Car:
def __init__(self, model, wheelColor, bodyColor, enginePower):
self.model = model
self.wheelColor = wheelColor
self.bodyColor = bodyColor
self.enginePower = enginePower
print(f"---{self.model}---차량")
print(f"{self.wheelColor}색의 바퀴")
print(f"{self.bodyColor}색의 몸통")
print(f"{self.enginePower}마력 엔진")
그리고 기본 기본 자동차의 경우는 menualDrive라는 기능을 가지고 있고 내부에서는 "수동으로 시동을 걸었습니다"라고 출력을 한다고 해보자.
class Car:
def __init__(self, model, wheelColor, bodyColor, enginePower):
self.model = model
self.wheelColor = wheelColor
self.bodyColor = bodyColor
self.enginePower = enginePower
print(f"---{self.model}---차량")
print(f"{self.wheelColor}색의 바퀴")
print(f"{self.bodyColor}색의 몸통")
print(f"{self.enginePower}마력 엔진")
def menualDrive(self):
print(f"수동으로 {self.model} 차량의 시동을 걸었습니다")
이때 객체를 만들어보면
car = Car("e50", "red", "blue", 400)
car.menualDrive()
===============출력=================
# ---e50---차량
# red색의 바퀴
# blue색의 몸통
# 400마력 엔진
# 수동으로 e50 차량의 시동을 걸었습니다
이렇게 출력되는 것을 볼 수 있다.
이제 자식 클래스를 한번 만들어서 상속을 해보자.
먼저 자식 클래스로는 GoldCar라는 클래스를 하나 만들어보자.
class GoldCar:
그리고 내부에 Car 클래스를 상속해주자.
class GoldCar(Car):
이제 이 GoldCar라는 클래스의 생성자를 하나 만들어주자.
class GoldCar(Car):
def __init__(self):
이제 이 생성자에서 부모 생성자처럼 모든 인자를 받아올 수 있도록 매개변수를 작성해주는데 여기서 우리는 추가로 windowColor라는 멤버변수를 추가해주자.
class GoldCar(Car):
def __init__(self, model, wheelColor, bodyColor, enginePower, windowColor):
이제 마지막 작업은 자식 생성자 내부에서 부모 생성자를 호출해서 값을 전달하는 일이다.
class GoldCar(Car):
def __init__(self, model, wheelColor, bodyColor, enginePower, windowColor):
super().__init__(model, wheelColor, bodyColor, enginePower)
self.windowColor =windowColor
print(f"---{self.model}---차량")
print(f"{self.wheelColor}색의 바퀴")
print(f"{self.bodyColor}색의 몸통")
print(f"{self.windowColor}색의 유리창")
print(f"{self.enginePower}마력 엔진")
이렇게 정의하면 부모클래스와 겹치는 자식클래스 멤버변수를 초기화하지 않더라도 부모클래스의 생성자에 의해서 초기화 되기 때문에 자식클래스에서도 원하는 대로 사용이 가능해진다.
한번 호출하는 문장을 만들어보면
goldCar = GoldCar("D30",
"RoseGold",
"WhiteGold",
500,
"BlackGold")
=======================================
---D30---차량
RoseGold색의 바퀴
WhiteGold색의 몸통
500마력 엔진
---D30---차량
RoseGold색의 바퀴
WhiteGold색의 몸통
BlackGold색의 유리창
500마력 엔진
보면 따로 정의 및 초기화하지 않더라도 자식클래스 내부에서도 멤버변수의 사용이 가능한것을 볼 수 있다.
### super().__init__()을 사용할때는 self를 넘겨줄 필요가 없고 특정 부모를 지정할 경우, Car.__init__(), self를 가장 첫번째 인자로 전달해줘야만 한다는 것을 알아두자.
클래스의 다중상속
자식 클래스에서 여러 부모 클래스를 상속하기 위해서는 괄호 내부의 상속 클래스를 , 콤마를 이용해서 다수 등록하면 된다.
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 클래스
여기서 부모의 생성자를 사용하는 경우의 예로도
class A:
def __init__(self, aMem):
self.aMem = aMem
def method_a(self):
print("A 클래스")
class B:
def __init__(self, bMem):
self.bMem = bMem
def method_b(self):
print("B 클래스")
class C(A, B):
def __init__(self ,aMem, bMem):
A.__init__(self, aMem)
B.__init__(self, bMem)
print(self.aMem)
print(self.bMem)
c = C("A Member", "B Member")
c.method_a() # A 클래스
c.method_b() # B 클래스
이렇게 사용이 가능하다.
다중 상속을 진행할때에는 super를 사용한다면 조금 설계가 명확해야하기에 쉽게 하기 위해서는 그냥 직접 부모를 호출하는 것이 가장 안전한 사용방식이라는 것을 알아두자.
### 이럴때는 super를 사용하는 것에 대해서 조금 더 알고 진행해야할것 같긴하다..