whatisthis?

Phython. 파이썬 코딩 기초편-08(下) 본문

WEB STUDY/PHYTHON | BACK-END

Phython. 파이썬 코딩 기초편-08(下)

thisisyjin 2021. 9. 18. 16:54
 

Phython. 파이썬 코딩 기초편-08(上)

당신의 회사에서는 매주 1회 보고서를 작성해야한다. 보고서는 항상 아래와 같은 형태로 출력되어야한다. - X 주차 주간 보고 - 부서 : 이름 : 업무 요약 : 1주차부터 50주차까지의 보고서 파일을

mywebproject.tistory.com

 

7. 메소드 오버라이딩

 

자식클래스에서 정의한 메소드를 사용하고 싶을 때, 메소드 오버라이딩을 사용.

 

 

1. Unit 클래스에 move 함수를 추가로 정의한다.

지상 유닛이 이동하는 경우의 move이다.

 

class Unit:
    def __init__(self, name, hp, speed):
        self.name = name
        self.hp = hp
        self.speed = speed
    
    def move(self, location):
        print("[지상 유닛 이동]")
        print("{0} : {1} 방향으로 이동합니다. [속도 {2}]".format(self.name, location, self.speed))

 

그리고, 이전에 날 수 있는 클래스였던 flyable 클래스에서 정의한

fly 함수의 경우에는 다음과 같았다.

 

class Flyable:
    def __init__(self, flying_speed):
        self.flying_speed = flying_speed

    def fly(self, name, location):
        print('{0} : {1} 방향으로 날아갑니다. [속도 {2}]'.format(name, location, self.flying_speed))

 

 

만약, 지상 유닛의 경우라면 Unit 클래스의 move함수를 이용해야하고,

공중 유닛의 경우에는 Flyable 클래스의 fly함수를 이용해야 한다.

 

그러나, 이런 경우에는 함수 이름이 다르기때문에 해당 유닛이 공중유닛인지 지상유닛인지 일일히 확인해야하는

불편함이 존재한다.

 

이때 해결하는 방법이 바로 메소드 오버라이딩 이다.

 

def move(self, location):
        print("[공중 유닛 이동]")
        self.fly(self.name, location)

다음과 같이 FlyableAttackUnit 클래스에 move함수를 추가해준다.

fly 함수의 경우에는 이름도 입력받도록 (name) 되어있으나

move함수에서는 self.name을 이용할 수 있도록 해보았다.

 

따라서

vulture.move("11시")

battlecruiser.fly("battlecrusier.name, "9시")

 

와 같이 move와 fly를 구분하여 작성해야했던 코드를

 

vulture.move("11시")

battlecruiser.move("9시")

 

와 같이 입력할 수 있다.

 

** 단, 

 

vulture = AttackUnit("벌쳐", 80, 10, 20)
battlecruiser = FlyableAttackUnit("배틀크루저", 500, 25, 3)

와 같이 이미 클래스로 선언된 객체이기 때문에 각자의 클래스에 맞춰 move함수를 실행한 것이다.

 

 

 

# 일반 유닛 
class Unit:
    def __init__(self, name, hp, speed):
        self.name = name
        self.hp = hp
        self.speed = speed
    
    def move(self, location):
        print("[지상 유닛 이동]")
        print("{0} : {1} 방향으로 이동합니다. [속도 {2}]".format(self.name, location, self.speed))

# 공격 유닛
class AttackUnit(Unit):
    def __init__(self, name, hp, speed, damage):
        Unit.__init__(self, name, hp, speed)
        self.damage = damage

    def attack(self, location):
        print("{0} : {1} 방향으로 적군을 공격합니다. [공격력 {2}]".format(self.name, location, self.damage))
        # name과 attack은 self값을 사용했으나, attack은 전달받은 값을 사용한다는 의미이다.
    
    def damaged(self, damage):
        print("{0} : {1} 데미지를 입었습니다.".format(self.name, damage))
        self.hp -= damage
        print("{0} : 현재 체력은 {1}입니다.".format(self.name, self.hp))
        if self.hp <= 0:
            print("{0} : 파괴되었습니다.".format(self.name))

# 날 수 있는 클래스
class Flyable:
    def __init__(self, flying_speed):
        self.flying_speed = flying_speed

    def fly(self, name, location):
        print('{0} : {1} 방향으로 날아갑니다. [속도 {2}]'.format(name, location, self.flying_speed))

# 공중 공격 유닛 클래스
class FlyableAttackUnit(AttackUnit, Flyable):
    def __init__(self, name, hp, damage, flying_speed):
        AttackUnit.__init__(self, name, hp, 0, damage) # 지상 스피드는 0
        Flyable.__init__(self, flying_speed)
    
    def move(self, location):
        print("[공중 유닛 이동]")
        self.fly(self.name, location)

# 벌쳐 : 지상 유닛. 스피디.
vulture = AttackUnit("벌쳐", 80, 10, 20)

# 배틀 크루저 : 공중 유닛. 체력,공격력 높음
battlecruiser = FlyableAttackUnit("배틀크루저", 500, 25, 3)

vulture.move("11시")
battlecruiser.move("9시")

실행 결과

 


8. pass

 

함수가 완성된것처럼 일단은 지나가라는 의미.

약간 continue 와 비슷한 개념.

그러나 이건 함수 내에 본문에 사용하는 것임.

 

 

예>

def game_start():
    print("새로운 게임을 시작합니다.")

def game_over():
    pass

game_start()
game_over()

                     


9. Super

class BuildingUnit(Unit):
    def __init__(self, name, hp, location):
        Unit.__init__(self, name, hp, 0) # speed는 0이므로
        self.location = location

원래는 상속을 할때

부모클래스명.__init__(self, 부모매소드들)과 같이 했는데,

super를 이용해서 할 수도 있다.

 

**참고 : Unit 클래스의 매소드 중에서 speed는 필요없으므로 0으로 적는다.

class BuildingUnit(Unit):
    def __init__(self, name, hp, location):
        super().__init__(name, hp, 0)
        self.location = location

작성 방법은

super().__init__(부모매소드들) 과 같다.

대신, super()을 이용할 때에는  self 를 적지 않아도 된다.

 

Unit.__init__(self, name, hp, 0)
super().__init__(name, hp, 0)

 

 

+) 다중 상속을 할 때 문제점

 

class Unit:
    def __init__(self):
        print("Unit 생성자")

class Flyable:
    def __init__(self):
        print("Flyable 생성자")

class FlyableUnit(Unit, Flyable):
    def __init__(self):
        super().__init__()

#드랍쉽
dropship = FlyableUnit()

위와 같이 간단한 버전의 예제를 만들어보자.

 

만약 두개 이상의 부모 클래스에게 상속을 받는다면(=다중상속)

super()를 이용하면 맨 처음에 오는 부모클래스에만 init함수가 호출이 된다.

 

 

** 다중 상속시에는 super()로 하면 안되고,

 원래의 방식대로 Unit.__init__(self, name, hp, 0) 와 같이 해줘야한다.

 


<Assignment> 스타크래프트 게임

 

Ch8에서 진행했던 스타크래프트 예제를 보고 살을 붙혀서

실제 스타크래프트처럼 (텍스트 기반) 플레이할 수 있도록 해보자!

 

 

1. 일반 유닛 = 공격은 못하지만 damaged함수(피해 받는것)는 가능하므로

기본 클래스인 unit 클래스에 damaged 함수를 붙여넣는다.

 

2. 각 유닛에 해당하는 클래스들을 만든다.

   1) 마린

   2) 탱크

   3) 레이스 (=공중 유닛)

 

 

# 일반 유닛 
class Unit:
    def __init__(self, name, hp, speed):
        self.name = name
        self.hp = hp
        self.speed = speed
        print("{0} 유닛이 생성되었습니다.".format(name)) #self.name으로 써도 ok
    
    def move(self, location):
        print("[지상 유닛 이동]")
        print("{0} : {1} 방향으로 이동합니다. [속도 {2}]".format(self.name, location, self.speed))

    def damaged(self, damage):
        print("{0} : {1} 데미지를 입었습니다.".format(self.name, damage))
        self.hp -= damage
        print("{0} : 현재 체력은 {1}입니다.".format(self.name, self.hp))
        if self.hp <= 0:
            print("{0} : 파괴되었습니다.".format(self.name))

# 공격 유닛
class AttackUnit(Unit):
    def __init__(self, name, hp, speed, damage):
        Unit.__init__(self, name, hp, speed)
        self.damage = damage

    def attack(self, location):
        print("{0} : {1} 방향으로 적군을 공격합니다. [공격력 {2}]".format(self.name, location, self.damage))
        # name과 attack은 self값을 사용했으나, attack은 전달받은 값을 사용한다는 의미이다.
    
# 마린
class Marine(AttackUnit):
    def __init__(self):
        AttackUnit.__init__(self, "마린", 40, 1, 5) 
    
    # 스팀팩 = 체력 10을 써서 이동및 공격속도 증가
    def stimpack(self):
        if self.hp > 10:
            self.hp -= 10
            print("{0} : 스팀팩을 사용합니다. (HP 10감소)".format(self.name))
        else:
            print("{0} : 체력이 부족하여 스팀팩을 사용하지 못합니다.".format(self.name))
    
# 탱크
class Tank(AttackUnit):
    # 시즈모드 = 탱크를 고정시켜 더 높은 공격력.
    seize_developed = False

    def __init__(self):
        AttackUnit.__init__(self, "탱크", 150, 1, 35)
        self.seize_mode = False
    

    def set_seize_mode(self):
        if Tank.seize_developed == False:
            return
        
        if self.seize_mode == False:
            print("{0} : 시즈모드로 전환합니다.".format(self.name))
            self.damage *= 2
            self.seize_mode = True

        else:
            print("{0} : 시즈모드로  해제합니다.".format(self.name))
            self.damage /= 2
            self.seize_mode = False





# 날 수 있는 클래스
class Flyable:
    def __init__(self, flying_speed):
        self.flying_speed = flying_speed

    def fly(self, name, location):
        print('{0} : {1} 방향으로 날아갑니다. [속도 {2}]'.format(name, location, self.flying_speed))

# 공중 공격 유닛 클래스
class FlyableAttackUnit(AttackUnit, Flyable):
    def __init__(self, name, hp, damage, flying_speed):
        AttackUnit.__init__(self, name, hp, 0, damage) # 지상 스피드는 0
        Flyable.__init__(self, flying_speed)
    
    def move(self, location):
        print("[공중 유닛 이동]")
        self.fly(self.name, location)


# 레이스
class Wraith(FlyableAttackUnit):
    def __init__(self):
        FlyableAttackUnit.__init__(self, "레이스", 80, 20, 5)
        self.clocked = False # 클로킹 모드

    def clocking(self):
        if self.clocked == False:
            print("{0} : 클로킹 모드로 전환합니다.".format(self.name))
            self.clocked == True

        else:
            print("{0} : 클로킹 모드를 해제합니다.".format(self.name))
            self.clocked == False

 

 

 

3. 실제 게임 플레이를 시뮬레이션

 

def game_start():
    print("[알림] 새로운 게임을 시작합니다.")

def game_over():
    print("Player : gg")
    print("[player] 님이 게임에서 퇴장하셨습니다.")



# 실제 게임 진행

game_start()


# 유닛 생성

m1 = Marine()
m2 = Marine()
m3 = Marine()

t1 = Tank()
t2 = Tank()

w1 = Wraith()

# 유닛 일괄 관리 (생성된 모든 유닛 append)
attack_units = []
attack_units.append(m1)     
attack_units.append(m2)
attack_units.append(m3)
attack_units.append(t1)
attack_units.append(t2)
attack_units.append(w1)

# 전군 이동

for unit in attack_units:
    unit.move("1시")

# 탱크 시즈모드 개발

Tank.seize_developed = True
print("[알림] 탱크 시즈 모드 개발이 완료되었습니다.")


# 공격 모드 준비 (스팀팩, 시즈모드, 클로킹)

for unit in attack_units:
    if isinstance(unit, Marine): 
        unit.stimpack()
    elif isinstance(unit, Tank):
        unit.set_seize_mode()
    elif isinstance(unit, Wraith):
        unit.clocking()

# 전군 공격

for unit in attack_units:
    unit.attackk("1시")

# 피해 입음

for unit in attack_units:
    unit.damaged(randint(5,20)) # 공격은 랜덤으로 받음


# 우리 유닛 다 죽음 - 게임종료
game_over()

 

 

 

<최종 실행 결과>

 

[알림] 새로운 게임을 시작합니다.
마린 유닛이 생성되었습니다.
마린 유닛이 생성되었습니다.
마린 유닛이 생성되었습니다.
탱크 유닛이 생성되었습니다.
탱크 유닛이 생성되었습니다.
레이스 유닛이 생성되었습니다.
[지상 유닛 이동]
마린 : 1시 방향으로 이동합니다. [속도 1]
[지상 유닛 이동]
마린 : 1시 방향으로 이동합니다. [속도 1]
[지상 유닛 이동]
마린 : 1시 방향으로 이동합니다. [속도 1]
[지상 유닛 이동]
탱크 : 1시 방향으로 이동합니다. [속도 1]
[지상 유닛 이동]
탱크 : 1시 방향으로 이동합니다. [속도 1]
[공중 유닛 이동]
레이스 : 1시 방향으로 날아갑니다. [속도 5]
[알림] 탱크 시즈 모드 개발이 완료되었습니다.
마린 : 스팀팩을 사용합니다. (HP 10감소)
마린 : 스팀팩을 사용합니다. (HP 10감소)
마린 : 스팀팩을 사용합니다. (HP 10감소)
탱크 : 시즈모드로 전환합니다.
탱크 : 시즈모드로 전환합니다.
레이스 : 클로킹 모드로 전환합니다.
마린 : 1시 방향으로 적군을 공격합니다. [공격력 5]    
마린 : 1시 방향으로 적군을 공격합니다. [공격력 5]    
마린 : 1시 방향으로 적군을 공격합니다. [공격력 5]    
탱크 : 1시 방향으로 적군을 공격합니다. [공격력 70]
탱크 : 1시 방향으로 적군을 공격합니다. [공격력 70]   
레이스 : 1시 방향으로 적군을 공격합니다. [공격력 20] 
마린 : 20 데미지를 입었습니다.
마린 : 현재 체력은 10입니다.
마린 : 15 데미지를 입었습니다.
마린 : 현재 체력은 15입니다.
마린 : 15 데미지를 입었습니다.
마린 : 현재 체력은 15입니다.
탱크 : 20 데미지를 입었습니다.
탱크 : 현재 체력은 130입니다.
탱크 : 14 데미지를 입었습니다.
탱크 : 현재 체력은 136입니다.
레이스 : 19 데미지를 입었습니다.
레이스 : 현재 체력은 61입니다.
Player : gg
[player] 님이 게임에서 퇴장하셨습니다.

 

 

<Quiz> 

 

주어진 코드로 부동산 프로그램을 작성하시오.

 

출력 예시.

총 3대의 매물이 있습니다.

강남 아파트 매매 10억 2010년
마포 오피스텔 전세 5억 2007년
송파 빌라 월세 500/50 2000년

 

[코드]

class House:

  # 매물 초기화
  def __init__(self, location, house_type, deal_type, price, year): 
       pass
       
  # 매물 정보 표시 
  def show_detail(self):
      pass