[2021년 10월 업데이트]

안녕하세요. 스쿨오브웹의 이상희입니다.

이번 강좌에서는 클래스의 상속과 서브 클래스에 대해서 알아보겠습니다.

이전 강좌에서 설명을 드렸듯이 클래스를 사용하는 이유는 네임스페이스를 이용하여 효율적으로 데이터를 관리하고, 같은 코드의 반복을 없애고자 하는 DRY(Don’t Repeat Yourself) 개념이 담겨 있습니다. 상속 또한 한 번 정의한 데이터 타입을 필요에 따라서 수정을 하고 다시 재활용해서 반복되는 코드를 줄이고자 하는 목적을 가지고 있습니다. 예제를 통하여 게임 케릭터의 클래스를 만들면서 설명을 하겠습니다. 만들고자 하는 케릭터는 다음과 같습니다.

  • 클래스: Hero
  • 랭크: 영웅
  • 라이프: 300
  • 사이즈: big
  • 설명: 상위 랭크의 케릭터로써 하위 랭크의 케릭터를 소환합니다.

  • 클래스: Goblin
  • 랭크: 병사
  • 라이프: 100
  • 사이즈: small
  • 설명: 하위 랭크의 케릭터로써 상위 랭크의 케릭터에 의해 소환됩니다.

가장 먼저 모든 케릭터가 가지게될 속성을 갖으며 모든 클래스의 베이스가 될 Unit 클래스를 만들고, Unit 클래스를 모든 속성을 상속받는 고블린 서브 클래스를 만들어 보겠습니다.

oop_5.py

class Unit:
    def __init__(self, rank, size, life):
        self.name = self.__class__.__name__
        self.rank = rank
        self.size = size
        self.life = life

    def show_status(self):
        print('이름: {}'.format(self.name))
        print('등급: {}'.format(self.rank))
        print('사이즈: {}'.format(self.size))
        print('라이프: {}'.format(self.life))


class Goblin(Unit):
    pass


goblin_1 = Goblin('병사', 'Small', 100)

goblin_1.show_status()
$ python oop_5.py
이름: Goblin
등급: 병사
사이즈: Small
라이프: 100

Unit 클래스의 하위 클래스인 Goblin 클래스는 어떠한 메소드나 속성을 정의하지 않았는데도 Unit 클래스안에 정의한 show_status 메소드를 상속받아 사용할 수 있었습니다. 그렇다면 Goblin 클래스가 자신의 네임스페이스 안에 show_status 메소드를 가지고 있을까요? 밑의 코드를 보시죠.

oop_5.py

class Unit:
    def __init__(self, rank, size, life):
        self.name = self.__class__.__name__
        self.rank = rank
        self.size = size
        self.life = life

    def show_status(self):
        print('이름: {}'.format(self.name))
        print('등급: {}'.format(self.rank))
        print('사이즈: {}'.format(self.size))
        print('라이프: {}'.format(self.life))


class Goblin(Unit):
    pass


goblin_1 = Goblin('병사', 'Small', 100)

print(Goblin.__dict__)
$ python oop_5.py
{'__doc__': None, '__module__': '__main__'}

위의 결과를 보시면 Goblin 클래스는 show_status 메소드를 실제로 가지고 있지 않습니다. 그렇다면 어떻게 호출을 했을까요? help() 함수를 이용하여 Goblin 클래스가 어떻게 show_status 메소드를 찾았는지 알아보죠.

oop_5.py

class Unit:
    def __init__(self, rank, size, life):
        self.name = self.__class__.__name__
        self.rank = rank
        self.size = size
        self.life = life

    def show_status(self):
        print('이름: {}'.format(self.name))
        print('등급: {}'.format(self.rank))
        print('사이즈: {}'.format(self.size))
        print('라이프: {}'.format(self.life))


class Goblin(Unit):
    pass

print(help(Goblin))
$ python oop_5.py
Help on class Goblin in module __main__:

class Goblin(Unit)
 |  Goblin(rank, size, life)
 |
 |  Method resolution order:
 |      Goblin
 |      Unit
 |      builtins.object
 |
 |  Methods inherited from Unit:
 |
 |  __init__(self, rank, size, life)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |
 |  show_status(self)
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors inherited from Unit:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)

None
 |  Method resolution order:
 |      Goblin
 |      Unit
 |      builtins.object

위의 설명은 Goblin 클래스가 이름을 찾는 순서를 보여주고 있습니다. 가장 먼저 자신의 네임 스페이스에서 찾고, 그 다음에 부모 클래스인 Unit 클래스의 네임 스페이스를 뒤지고, 마지막으로 최상 클래스인 내장 오브젝트 클래스의 네임 스페이스를 참조하는군요. 즉, 인스턴스 생성시에 무조건 호출되어야 하는 init() 메소드도 자신의 네임스페이스에서 찾지 못 하면 부모 클래스에서 참조를 하는 것입니다.

 |  Methods inherited from Unit:
 |
 |  __init__(self, rank, size, life)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |
 |  show_status(self)

위의 설명을 보시면 init()과 show_status() 메소드를 Unit 클래스로 부터 상속 받았다고 보여주네요. 결국 자기의 네임 스페이스 안에는 가지고 있지 않지만 상위 클래스의 네임 스페이스를 참조하는 것이지요.

dir() 함수를 이용하여 Goblin 클래스가 참조 할 수 있는 정보를 확인해 보죠.

oop_5.py

class Unit:
    def __init__(self, rank, size, life):
        self.name = self.__class__.__name__
        self.rank = rank
        self.size = size
        self.life = life

    def show_status(self):
        print('이름: {}'.format(self.name))
        print('등급: {}'.format(self.rank))
        print('사이즈: {}'.format(self.size))
        print('라이프: {}'.format(self.life))


class Goblin(Unit):
    pass


print(dir(Goblin))
$ python oop_5.py
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'show_status']

이번에는 Goblin 클래스에 새로운 속성인 “공격 타입”을 추가하고자 합니다.

oop_5.py

class Unit:
    def __init__(self, rank, size, life):
        self.name = self.__class__.__name__
        self.rank = rank
        self.size = size
        self.life = life

    def show_status(self):
        print('이름: {}'.format(self.name))
        print('등급: {}'.format(self.rank))
        print('사이즈: {}'.format(self.size))
        print('라이프: {}'.format(self.life))


class Goblin(Unit):
    def __init__(self, rank, size, life, attack_type):
        self.name = self.__class__.__name__
        self.rank = rank
        self.size = size
        self.life = life
        self.attack_type = attack_type

    def show_status(self):
        print('이름: {}'.format(self.name))
        print('등급: {}'.format(self.rank))
        print('사이즈: {}'.format(self.size))
        print('라이프: {}'.format(self.life))
        print('공격 타입: {}'.format(self.attack_type))

goblin_1 = Goblin('병사', 'Small', 100, '근접 공격')

goblin_1.show_status()
$ python oop_5.py
이름: Goblin
등급: 병사
사이즈: Small
라이프: 100
공격 타입: 근접 공격

자신의 클래스 안에 init() 메소드와 show_status() 메소드를 다시 정의하여 원하는 속성과 기능을 추가하였으나, Unit 클래스에서 정의한 모든 코드를 반복한 것을 알 수 있습니다. 위의 코드같이 코딩을 하면 클래스를 사용하는 의미가 전혀 없습니다. 아주 나쁜 코딩의 좋은 예라고 할 수 있습니다. ^^;;

이렇게 상위 클래스의 메소드를 자신의 클래스 안에 다시 정의하는 것을 메소드 오버라이드라고 하며, 이런 경우에는 super() 함수를 사용하여 이미 부모 클래스에서 정의된 메소드를 호출하여 같은 코드를 반복하지 않도록 합니다. super() 함수를 사용하여 중복된 코드를 줄여 보겠습니다.

oop_5.py

class Unit:
    def __init__(self, rank, size, life):
        self.name = self.__class__.__name__
        self.rank = rank
        self.size = size
        self.life = life

    def show_status(self):
        print('이름: {}'.format(self.name))
        print('등급: {}'.format(self.rank))
        print('사이즈: {}'.format(self.size))
        print('라이프: {}'.format(self.life))


class Goblin(Unit):
    def __init__(self, rank, size, life, attack_type):
        super(Goblin, self).__init__(rank, size, life)
        self.attack_type = attack_type

    def show_status(self):
        super(Goblin, self).show_status()
        print('공격 타입: {}'.format(self.attack_type))


goblin_1 = Goblin('병사', 'Small', 100, '근접 공격')

goblin_1.show_status()
$ python oop_5.py
이름: Goblin
등급: 병사
사이즈: Small
라이프: 100
공격 타입: 근접 공격

어떤가요? 코드가 아주 간단해지지 않았나요? 이것이 메소드 오버라이드(method override)입니다.

고블린 클래스에 공격 메소드와 데미지 속성을 추가하겠습니다. 그리고 Goblin을 클래스를 상속받는 SphereGoblin 클래스를 만들고 sphere_type이라는 속성을 추가하겠습니다.

oop_5.py

class Unit:
    def __init__(self, rank, size, life):
        self.name = self.__class__.__name__
        self.rank = rank
        self.size = size
        self.life = life

    def show_status(self):
        print('이름: {}'.format(self.name))
        print('등급: {}'.format(self.rank))
        print('사이즈: {}'.format(self.size))
        print('라이프: {}'.format(self.life))


class Goblin(Unit):
    # damage 속성 추가
    def __init__(self, rank, size, life, attack_type, damage):
        super(Goblin, self).__init__(rank, size, life)
        self.attack_type = attack_type
        self.damage = damage

    def show_status(self):
        super(Goblin, self).show_status()
        print('공격 타입: {}'.format(self.attack_type))
        # 오버라이드 메소드
        print('데미지: {}'.format(self.damage))

    # 공격 메소드 추가
    def attack(self):
        print('[{}]이 공격합니다! 상대방 데미지({})'.format(self.name, self.damage))


class SphereGoblin(Goblin):
    def __init__(self, rank, size, life, attack_type, damage, sphere_type):
        super(SphereGoblin, self).__init__(rank, size, life, attack_type, damage)
        self.sphere_type = sphere_type

    def show_status(self):
        super(SphereGoblin, self).show_status()
        print('창 타입: {}'.format(self.sphere_type))


sphere_goblin_1 = SphereGoblin('병사', 'Small', 100, '레인지 공격', 10, '긴 창')

sphere_goblin_1.show_status()
$ python oop_5.py
이름: SphereGoblin
등급: 병사
사이즈: Small
라이프: 100
공격 타입: 레인지 공격
데미지: 10
창 타입: 긴 창

SphereGoblin 클래스는 부모 클래스인 Goblin 클래스와 할아버지 클래스인 Unit 클래스에서 모든 속성과 메소드를 상속받은 것을 볼 수 있습니다.

이번에는 Goblin 케릭터를 거느릴 Hero 클래스를 만들어 보겠습니다.

oop_5.py

class Unit:
    def __init__(self, rank, size, life):
        self.name = self.__class__.__name__
        self.rank = rank
        self.size = size
        self.life = life

    def show_status(self):
        print('이름: {}'.format(self.name))
        print('등급: {}'.format(self.rank))
        print('사이즈: {}'.format(self.size))
        print('라이프: {}'.format(self.life))


class Goblin(Unit):
    # damage 속성 추가
    def __init__(self, rank, size, life, attack_type, damage):
        super(Goblin, self).__init__(rank, size, life)
        self.attack_type = attack_type
        self.damage = damage

    def show_status(self):
        super(Goblin, self).show_status()
        print('공격 타입: {}'.format(self.attack_type))
        # 오버라이드 메소드
        print('데미지: {}'.format(self.damage))

    # 공격 메소드 추가
    def attack(self):
        print('[{}]이 공격합니다! 상대방 데미지({})'.format(self.name, self.damage))


class SphereGoblin(Goblin):
    def __init__(self, rank, size, life, attack_type, damage, sphere_type):
        super(SphereGoblin, self).__init__(rank, size, life, attack_type, damage)
        self.sphere_type = sphere_type

    def show_status(self):
        super(SphereGoblin, self).show_status()
        print('창 타입: {}'.format(self.sphere_type))


class Hero(Unit):
    def __init__(self, rank, size, life, goblins=None):
        super(Hero, self).__init__(rank, size, life)
        if goblins is None:
            self.goblins = []
        else:
            self.goblins = goblins

    def show_own_goblins(self):
        num_of_goblins = len([x for x in self.goblins if isinstance(x, Goblin)])
        num_of_sphere_goblins = len([x for x in self.goblins if isinstance(x, SphereGoblin)])
        print('현재 영웅이 소유한 고블린은 {}명, 창 고블린은 {}명입니다.'.format(num_of_goblins, num_of_sphere_goblins))

    def make_goblins_attack(self):
        for goblin in self.goblins:
            goblin.attack()


# 고블린 오브젝트 생성
goblin_1 = Goblin('병사', 'Small', 100, '근접 공격', 15)
goblin_2 = Goblin('병사', 'Small', 100, '근접 공격', 15)
sphere_goblin_1 = SphereGoblin('병사', 'Small', 100, '레인지 공격', 10, '긴 창')

# 영웅 오브젝트 생성 후, 고블린 오브젝트 할당       
hero_1 = Hero('영웅', 'Big', 300, [goblin_1, goblin_2, sphere_goblin_1])
hero_1.show_own_goblins()
hero_1.make_goblins_attack()
$ python oop_5.py
현재 영웅이 소유한 고블린은 3명, 창 고블린은 1명입니다.
[Goblin]이 공격합니다! 상대방 데미지(15)
[Goblin]이 공격합니다! 상대방 데미지(15)
[SphereGoblin]이 공격합니다! 상대방 데미지(10)

가장 상위 클래스인 Unit 클래스로 부터 상속을 받는 Hero 클래스를 만들고 __init__()를 오버라이딩했습니다. 그리고, 자신이 소유하고 있는 고블린의 수를 표시하는 메소드와 소유한 고블린이 공격을 하도록 하는 메소드도 만들어 봤습니다. 이번에는 고블린을 추가하고 없애는 메소드를 만들어 보겠습니다.

oop_5.py

class Unit:
    def __init__(self, rank, size, life):
        self.name = self.__class__.__name__
        self.rank = rank
        self.size = size
        self.life = life

    def show_status(self):
        print('이름: {}'.format(self.name))
        print('등급: {}'.format(self.rank))
        print('사이즈: {}'.format(self.size))
        print('라이프: {}'.format(self.life))


class Goblin(Unit):
    # damage 속성 추가
    def __init__(self, rank, size, life, attack_type, damage):
        super(Goblin, self).__init__(rank, size, life)
        self.attack_type = attack_type
        self.damage = damage

    def show_status(self):
        super(Goblin, self).show_status()
        print('공격 타입: {}'.format(self.attack_type))
        # 오버라이드 메소드
        print('데미지: {}'.format(self.damage))

    # 공격 메소드 추가
    def attack(self):
        print('[{}]이 공격합니다! 상대방 데미지({})'.format(self.name, self.damage))


class SphereGoblin(Goblin):
    def __init__(self, rank, size, life, attack_type, damage, sphere_type):
        super(SphereGoblin, self).__init__(rank, size, life, attack_type, damage)
        self.sphere_type = sphere_type

    def show_status(self):
        super(SphereGoblin, self).show_status()
        print('창 타입: {}'.format(self.sphere_type))


class Hero(Unit):
    def __init__(self, rank, size, life, goblins=None):
        super(Hero, self).__init__(rank, size, life)
        if goblins is None:
            self.goblins = []
        else:
            self.goblins = goblins

    def show_own_goblins(self):
        num_of_goblins = len([x for x in self.goblins if isinstance(x, Goblin)])
        num_of_sphere_goblins = len([x for x in self.goblins if isinstance(x, SphereGoblin)])
        print('현재 영웅이 소유한 고블린은 {}명, 창 고블린은 {}명입니다.'.format(
            num_of_goblins - num_of_sphere_goblins, num_of_sphere_goblins
        ))

    def make_goblins_attack(self):
        for goblin in self.goblins:
            goblin.attack()

    def add_goblins(self, new_goblins):
        for goblin in new_goblins:
            if goblin not in self.goblins:
                self.goblins.append(goblin)
            else:
                print('이미 추가된 고블린입니다.')

    def remove_goblins(self, old_goblins):
        for goblin in old_goblins:
            try:
                self.goblins.remove(goblin)
            except:
                print('{}을 소유하고 있지 않습니다.'.format(goblin))


# 고블린 오브젝트 생성
goblin_1 = Goblin('병사', 'Small', 100, '근접 공격', 15)
goblin_2 = Goblin('병사', 'Small', 100, '근접 공격', 15)
sphere_goblin_1 = SphereGoblin('병사', 'Small', 100, '레인지 공격', 10, '긴 창')

# 영웅 오브젝트 생성 후, 고블린 오브젝트 할당       
hero_1 = Hero('영웅', 'Big', 300, [goblin_1, goblin_2, sphere_goblin_1])

# 새로운 고블린 생성
goblin_3 = Goblin('병사', 'Small', 100, '근접 공격', 20)
sphere_goblin_2 = SphereGoblin('병사', 'Small', 100, '레인지 공격', 5, '긴 창')

print('# 새로운 고블린 추가 전')
hero_1.show_own_goblins()
hero_1.make_goblins_attack()

# 새로운 고블린 추가
hero_1.add_goblins([goblin_3, sphere_goblin_2])

print('\n# 새로운 고블린 추가 후')
hero_1.show_own_goblins()
hero_1.make_goblins_attack()

# 추가한 고블린 삭제
hero_1.remove_goblins([goblin_3, sphere_goblin_2])

print('\n# 추가한 고블린 삭제 후')
hero_1.show_own_goblins()
hero_1.make_goblins_attack()
$ python oop_5.py
# 새로운 고블린 추가 전
현재 영웅이 소유한 고블린은 2명, 창 고블린은 1명입니다.
[Goblin]이 공격합니다! 상대방 데미지(15)
[Goblin]이 공격합니다! 상대방 데미지(15)
[SphereGoblin]이 공격합니다! 상대방 데미지(10)

# 새로운 고블린 추가 후
현재 영웅이 소유한 고블린은 3명, 창 고블린은 2명입니다.
[Goblin]이 공격합니다! 상대방 데미지(15)
[Goblin]이 공격합니다! 상대방 데미지(15)
[SphereGoblin]이 공격합니다! 상대방 데미지(10)
[Goblin]이 공격합니다! 상대방 데미지(20)
[SphereGoblin]이 공격합니다! 상대방 데미지(5)

# 추가한 고블린 삭제 후
현재 영웅이 소유한 고블린은 2명, 창 고블린은 1명입니다.
[Goblin]이 공격합니다! 상대방 데미지(15)
[Goblin]이 공격합니다! 상대방 데미지(15)
[SphereGoblin]이 공격합니다! 상대방 데미지(10)

이미 추가된 고블린을 추가하고, 소유하지 않는 고블린을 지워서 에러 메세지를 발생시켜 보겠습니다.

oop_5.py

class Unit:
    def __init__(self, rank, size, life):
        self.name = self.__class__.__name__
        self.rank = rank
        self.size = size
        self.life = life

    def show_status(self):
        print('이름: {}'.format(self.name))
        print('등급: {}'.format(self.rank))
        print('사이즈: {}'.format(self.size))
        print('라이프: {}'.format(self.life))


class Goblin(Unit):
    # damage 속성 추가
    def __init__(self, rank, size, life, attack_type, damage):
        super(Goblin, self).__init__(rank, size, life)
        self.attack_type = attack_type
        self.damage = damage

    def show_status(self):
        super(Goblin, self).show_status()
        print('공격 타입: {}'.format(self.attack_type))
        # 오버라이드 메소드
        print('데미지: {}'.format(self.damage))

    # 공격 메소드 추가
    def attack(self):
        print('[{}]이 공격합니다! 상대방 데미지({})'.format(self.name, self.damage))


class SphereGoblin(Goblin):
    def __init__(self, rank, size, life, attack_type, damage, sphere_type):
        super(SphereGoblin, self).__init__(rank, size, life, attack_type, damage)
        self.sphere_type = sphere_type

    def show_status(self):
        super(SphereGoblin, self).show_status()
        print('창 타입: {}'.format(self.sphere_type))


class Hero(Unit):
    def __init__(self, rank, size, life, goblins=None):
        super(Hero, self).__init__(rank, size, life)
        if goblins is None:
            self.goblins = []
        else:
            self.goblins = goblins

    def show_own_goblins(self):
        num_of_goblins = len([x for x in self.goblins if isinstance(x, Goblin)])
        num_of_sphere_goblins = len([x for x in self.goblins if isinstance(x, SphereGoblin)])
        print('현재 영웅이 소유한 고블린은 {}명, 창 고블린은 {}명입니다.'.format(num_of_goblins, num_of_sphere_goblins))

    def make_goblins_attack(self):
        for goblin in self.goblins:
            goblin.attack()

    def add_goblins(self, new_goblins):
        for goblin in new_goblins:
            if goblin not in self.goblins:
                self.goblins.append(goblin)
            else:
                print('이미 추가된 고블린입니다.')

    def remove_goblins(self, old_goblins):
        for goblin in old_goblins:
            try:
                self.goblins.remove(goblin)
            except:
                print('소유하고 있지 않은 고블린입니다.')


# 고블린 오브젝트 생성
goblin_1 = Goblin('병사', 'Small', 100, '근접 공격', 15)
goblin_2 = Goblin('병사', 'Small', 100, '근접 공격', 15)
sphere_goblin_1 = SphereGoblin('병사', 'Small', 100, '레인지 공격', 10, '긴 창')

# 영웅 오브젝트 생성 후, 고블린 오브젝트 할당       
hero_1 = Hero('영웅', 'Big', 300, [goblin_1, goblin_2, sphere_goblin_1])

# 새로운 고블린 생성
goblin_3 = Goblin('병사', 'Small', 100, '근접 공격', 20)
sphere_goblin_2 = SphereGoblin('병사', 'Small', 100, '레인지 공격', 5, '긴 창')

print('# 새로운 고블린 추가 전')
hero_1.show_own_goblins()
hero_1.make_goblins_attack()

# 새로운 고블린 추가
hero_1.add_goblins([goblin_3, sphere_goblin_2])

print('\n# 새로운 고블린 추가 후')
hero_1.show_own_goblins()
hero_1.make_goblins_attack()

# 추가한 고블린 삭제
hero_1.remove_goblins([goblin_3, sphere_goblin_2])

print('\n# 추가한 고블린 삭제 후')
hero_1.show_own_goblins()
hero_1.make_goblins_attack()

# 영웅에게 소유되지 않은 고블린 생성
goblin_4 = Goblin('병사', 'Small', 100, '근접 공격', 20)

# 이미 소유한 고블린 추가
print('\n# 에러 메세지 발생')
hero_1.add_goblins([goblin_1])
hero_1.remove_goblins([goblin_4])
$ python oop_5.py
# 새로운 고블린 추가 전
현재 영웅이 소유한 고블린은 3명, 창 고블린은 1명입니다.
[Goblin]이 공격합니다! 상대방 데미지(15)
[Goblin]이 공격합니다! 상대방 데미지(15)
[SphereGoblin]이 공격합니다! 상대방 데미지(10)

# 새로운 고블린 추가 후
현재 영웅이 소유한 고블린은 5명, 창 고블린은 2명입니다.
[Goblin]이 공격합니다! 상대방 데미지(15)
[Goblin]이 공격합니다! 상대방 데미지(15)
[SphereGoblin]이 공격합니다! 상대방 데미지(10)
[Goblin]이 공격합니다! 상대방 데미지(20)
[SphereGoblin]이 공격합니다! 상대방 데미지(5)

# 추가한 고블린 삭제 후
현재 영웅이 소유한 고블린은 3명, 창 고블린은 1명입니다.
[Goblin]이 공격합니다! 상대방 데미지(15)
[Goblin]이 공격합니다! 상대방 데미지(15)
[SphereGoblin]이 공격합니다! 상대방 데미지(10)

# 에러 메세지 발생
이미 추가된 고블린입니다.
소유하고 있지 않은 고블린입니다.

에러 메세지도 문제 없이 출력되네요.

이번 강좌는 여기서 마치겠습니다. 설명이 조금 장황해졌습니다만, 클래스의 상속과 서브 클래스를 만드는 방법에 대해서 조금 더 이해가 되셨을거라 생각합니다. 프로그램은 책이나 글을 읽으며 머리로 이해하는 것도 중요하지만, 직접 코딩을 하고 문제가 발생하면 검색을 하거나 다른 사람에게 물어서 문제를 풀면서 손으로 익히는 것이 더 중요하다고 생각합니다. 많은 클래스를 만들어 보시고, 어떤 데이터를 클래스로 만들 수 있는지 많이 생각해 보십시오.

수고하셨습니다. 그럼, 해피코딩하세요~!