Python 기초

상속 구현하기

부모 클래스와 자식 클래스

아주 간단한 예를 들어봅시다. 우리는 일반적인 차량을 나타내는 Vehicle을 상속받아서 트럭을 나타내는 Truck 클래스를 생성해봅시다.

여기서 Vehicle을 부모 클래스(parent class)라고 하고 Truck을 자식 클래스(child class)라고 합니다.

자식 클래스는 부모 클래스의 특별한 버전이라고 생각하여도 됩니다. 반대로 부모 클래스는 자식 클래스의 일반화된 버전입니다.

# 자식 클래스는 서브 클래스라고도 합니다.
# 부모 클래스는 슈퍼 클래스라고도 합니다.
class 자식 클래스 (부모 클래스) :
    생성자
    메소드

 

UML에서 상속은 아래 그림처럼 자식 클래스에서 부모 클래스로 속이 빈 화살표를 그려서 표시합니다. 화살표의 모양과 방향에 주의해야 합니다.

uml inheritance vehicle에 대한 이미지 검색결과

 

일반적인 차량을 나타내는 Vehicle 클래스를 파이썬으로 작성해보면 아래와 같습니다.

# 일반적인 운송 수단을 나타내는 클래스입니다.
class Vehicle:
    def __init__(self, make, model, color, price):
        self.make = make        # 메이커
        self.model = model      # 모델
        self.color = color      # 자동차의 색상
        self.price = price      # 자동차의 가격

    def setMake(self, make):    # 설정자 메소드
        self.make = make

    def getMake(self):          # 접근자 메소드
        return self.make

    # 차량에 대한 정보를 문자열로 요약하여서 반환합니다.
    def getDesc(self):
        return "차량 = (" + str(self.make) + ", " + \
            str(self.model) + "," + str(self.color)  + \
            ", " + str(self.price) + ")"

 

Vehicle의 __init() 메소드는 make, model, color, price와 같은 매개변수를 가집니다.

이들 매개변수를 통하여 전달된 값은 객체 안의 인스턴스 변수들을 초기화하는데 사용됩니다. Vehicle 클래스의 인스턴스 변수는 아래와 같습니다.

  • make : 제조사를 나타내는 인스턴스 변수 (문자열)
  • model : 차량 모델을 나타내는 인스턴스 변수 (문자열)
  • color : 차량 색상을 나타내는 인스턴스 변수 (문자열)
  • price : 차량 가격을 나타내는 인스턴스 변수 (정수형)

 

위의 예제에서는 인스턴스 변수 중에서 make에 대해서만 접근자와 설정저가 선언되어 있습니다.

 

NOTE

앞에서 배웠듯이 인스턴스 변수 앞에 __ 을 붙이면 private 변수가 되어서 외부에서 접근할 수 없습니다. 이 경우, 외부에서 접근하려면 접근자 메소드와 설정자 메소드를 선언하여야 합니다.

여기서는 편의상 인스턴스 변수 앞에 __을 붙이지 않았습니다.

 

Vehicle 클래스는 완전한 클래스여서 이 클래스를 가지고 객체를 생성할 수도 있습니다. 그러나 이 클래스는 차량에 대한 일반적인 정보만을 저장하고 있습니다.

각 차량에 대한 세부적인 정보는 빠져 있을 수 있습니다. 따라서 이 클래스로부터 상속을 받아서 클래스를 정의해봅시다.

# 일반적인 운송 수단을 나타내는 클래스입니다.
class Vehicle:
    def __init__(self, make, model, color, price):
        self.make = make        # 메이커
        self.model = model      # 모델
        self.color = color      # 자동차의 색상
        self.price = price      # 자동차의 가격

    def setMake(self, make):    # 설정자 메소드
        self.make = make

    def getMake(self):          # 접근자 메소드
        return self.make

    # 차량에 대한 정보를 문자열로 요약하여서 반환합니다.
    def getDesc(self):
        return "차량 = (" + str(self.make) + ", " + \
            str(self.model) + "," + str(self.color)  + \
            ", " + str(self.price) + ")"

class Truck(Vehicle):    # 문장 1
    def __init__(self, make, model, color, price, payload):
        super().__init__(make, model, color, price)    # 문장 2
        self.payload = payload    # 문장 3

    def setPayload(self, payload):      # 설정자 메소드
        self.payload = payload

    def getPayload(self):               # 접근자 메소드
        return self.payload

 

Truck 클래스의 첫 번째 줄에서 상속이 정의되고 있습니다.

  • 문장 1은 우리가 Truck이라는 클래스를 정의하는데 Vehicle로부터 상속받는다는 의미입니다. 이때는 Truck 클래스가 자식 클래스가 되고 Vehicle 클래스가 부모 클래스가 됩니다.

        우리는 Truck 클래스도 Vehicle 클래스의 일종이라고 말할 수 있습니다. 왜나하면 Truck 클래스는 Vehicle 클래스로부터 모든 메소드와 인스턴스 변수를 상속받았기 때문입니다.

  • 문장 2에서 부모 클래스인 Vehicle의 생성자를 호출하고 있습니다.
  • 문장 3에서 Truck 클래스에 트럭의 적재 용량을 나타내는 payload라는 인스턴스 변수를 추가하고 있습니다.

 

무엇이 상속되는가?

상속을 사용하면 과연 무엇이 상속되는 것일까요? 부모 클래스의 인스턴스 변수와 메소드가 자식 클래스로 상속됩니다. 따라서 자식 클래스는 부모 클래스의 인스턴스 변수와 메소드를 자유롭게 사용할 수 있습니다.

자식 클래스는 필요하면 자신만의 인스턴스 변수와 메소드를 추가시킬 수도 있고 부모 클래스에 이미 존재하는 메소드를 새롭게 정의하여 사용할 수도 있습니다.

상속의 강점은 부모 클래스로부터 상속된 특징들을 자식 클래스에서 추가, 교체, 상세화시킬 수 있는 능력으로부터 나옵니다.

inheritance에 대한 이미지 검색결과

 

inheritance vehicle truck에 대한 이미지 검색결과

 

자식 클래스는 부모 클래스가 가지고 있는 모든 멤버들을 전부 상속받고 자신이 필요한 멤버를 추가하기 때문에 항상 자식 클래스가 부모 클래스를 포함하게 됩니다.

위의 그림에서 Truck 클래스는 Vehicle 클래스의 모든 인스턴스 변수와 메소드를 상속 받고 여기에 하나의 인스턴스 변수를 추가하였습니다. 상속을 하게 되면 멤버가 유지 또는 증가합니다.

 

자 이제부터 Truck 클래스의 객체를 생성하여 상속받은 인스턴스 변수와 메소드를 사용하여 봅시다. 상속이 올바르게 되었는지를 테스트하는 코드는 아래와 같습니다.

# 일반적인 운송 수단을 나타내는 클래스입니다.
class Vehicle:
    def __init__(self, make, model, color, price):
        self.make = make        # 메이커
        self.model = model      # 모델
        self.color = color      # 자동차의 색상
        self.price = price      # 자동차의 가격

    def setMake(self, make):    # 설정자 메소드
        self.make = make

    def getMake(self):          # 접근자 메소드
        return self.make

    # 차량에 대한 정보를 문자열로 요약하여서 반환합니다.
    def getDesc(self):
        return "차량 = (" + str(self.make) + ", " + \
            str(self.model) + "," + str(self.color) + \
            ", " + str(self.price) + ")"

class Truck(Vehicle):
    def __init__(self, make, model, color, price, payload):
        super().__init__(make, model, color, price)
        self.payload = payload

    def setPayload(self, payload):      # 설정자 메소드
        self.payload = payload

    def getPayload(self):               # 접근자 메소드
        return self.payload

def main():
    myTruck = Truck("Tesla", "Model S", "white", 10000, 2000)

    myTruck.setMake("Tesla")            # 설정자 메소드 호출
    myTruck.setPayload(2000)            # 설정자 메소드 호출
    print(myTruck.getDesc())            # 트럭 객체를 문자열로 출력

main()

 

<실행 결과>

차량 = (Tesla, Model S,white, 10000)

 

자식 클래스는 부모 클래스의 인스턴스 변수와 메소드를 마치 자기 것처럼 사용할 수 있습니다. 예를 들어서 myTruck은 부모 클래스의 인스턴스 변수인 make를 마음대로 사용할 수 있습니다.

부모 클래스에 선언된 setMake() 메소드도 마음대로 사용할 수 있습니다. 자식 클래스가 정의한 자체 메소드인 setPayload()를 사용할 수 있음은 물론입니다.

 

상속을 이용하지 않고 작성하였다면 Truck 클래스는 5개의 인스턴스 변수와 5개의 메소드를 가진 클래스로 새로 작성하여야 했을 것입니다.

여기서는 간단하게 설명하기 위하여 Vehicle의 멤버를 대폭 줄였지만 만약 Vehicle이 100개의 멤버를 가지는 복잡한 클래스였다면 상속 없이 Truck을 작성하는 것도 만만치 않았을 것입니다.

여기서 얻은 결론은 상속은 시간을 절약하고 버그를 줄여주는 소중한 기법이라는 것입니다.