goodthings4me.tistory.com
[파이썬 클래스 연습] 객체 지향 개발 언어 스터디 중 난해하다고 하는 클래스 개념을 이해하기 위해 패스트캠퍼스의 파이썬 웹 개발에서 이즈리얼, 리신, 몬스터 예시로 설명하는 '클래스 개념' 강의 내용을 정리했다.
클래스를 사용하는 이유 - RPG 게임을 활용하여 설명
■ 클래스를 사용하지 않았을 때
- 공격 함수를 하나 만들고 챔피언 '이즈리얼', '리신'이 이를 사용한다.
# 공격 함수
def basic_attack(name, attack):
print(f'{name} 기본 공격: {attack}')
champion1_name = '이즈리얼'
champion1_health = 700
champion1_attack = 90
print(f'{champion1_name}님 소환사의 협곡에 오신 것을 환영합니다.')
basic_attack(champion1_name, champion1_attack)
champion2_name = '리신'
champion2_health = 800
champion2_attack = 95
print(f'{champion2_name}님 소환사의 협곡에 오신 것을 환영합니다.')
basic_attack(champion2_name, champion2_attack)
[실행 결과]
이즈리얼님 소환사의 협곡에 오신 것을 환영합니다.
이즈리얼 기본 공격: 90
리신님 소환사의 협곡에 오신 것을 환영합니다.
리신 기본 공격: 95
- 챔피언 '야스오' 하나를 더 만든다.
champion3_name = '야스오'
champion3_health = 800
champion3_attack = 95
print(f'{champion3_name}님 소환사의 협곡에 오신 것을 환영합니다.')
basic_attack(champion3_name, champion3_attack)
[실행 결과]
야스오님 소환사의 협곡에 오신 것을 환영합니다.
야스오 기본 공격: 95
클래스를 사용하지 않을 때는 챔피언을 추가할 때마다 5줄의 코딩이 추가되었다.
■ 클래스를 사용할 때
## Champion 클래스
class Champion:
def __init__(self, name, health, attack):
self.name = name
self.health = health
self.attack = attack
print(f'{self.name}님 소환사의 협곡에 오신 것을 환영합니다.')
def basic_attack(self):
print(f'{self.name} 기본 공격: {self.attack}')
# 챔피언 이즈리얼 객체 생성
champion1_ezreal = Champion('이즈리얼', 700, 90)
champion1_ezreal.basic_attack()
# 챔피언 리신 객체 생성
champion2_leesin = Champion('리신', 800, 95)
champion2_leesin.basic_attack()
[실행 결과]
이즈리얼님 소환사의 협곡에 오신 것을 환영합니다.
이즈리얼 기본 공격: 90
리신님 소환사의 협곡에 오신 것을 환영합니다.
리신 기본 공격: 95
- 챔피언 '야스오' 객체를 만든다.
# 객체 추가
champion3_yasuo = Champion('야스오', 770, 96)
champion3_yasuo.basic_attack()
[실행 결과]
야스오님 소환사의 협곡에 오신 것을 환영합니다.
야스오 기본 공격: 96
챔피언 1,000개를 구현한다면,
클래스 미사용 시, 코드수 5줄 * 1,000개 = 5,000줄
클래스 사용 시, 2줄 * 1,000개 = 2,000줄
클래스를 사용하지 않는 경우는 챔피언을 추가할 때마다 동일한 코드량을 추가하지만, 클래스를 사용할 때는 객체만 만들면 되기 때문에 수백수천의 많은 챔피언을 만든다고 가정하면 클래스를 이용하는 방식이 더 효율적이다. (중복 코드 최소화)
■ 클래스의 효용성 측면과 클래스 생성자, 상속, 오버라이딩에 대해 설명
# Monster 클래스
class Monster:
def __init__(self, health, attack, speed): # 속성 초기화
self.health = health
self.attack = attack
self.speed = speed
def decrease_health(self, num): # 체력 감소
self.health -= num
def get_health(self): # 남은 체력 얻기
return self.health
goblin = Monster(800, 100, 300)
wolf = Monster(1000, 300, 400)
goblin.decrease_health(50)
print(goblin.get_health())
[실행 결과]
750
- Monster 클래스 내에는 __init__() (이를 생성자라 함), decrease_health(), get_health() 메서드가 있다.
- goblin과 wolf 객체를 만들 때, 속성 초기화를 위해서 생성자에 매개변수 3개(health, attack, speed)를 넣어 생성했다.
- 생성된 객체 goblin이 decrease_health() 메서드를 호출(체력 50 사용)하였고, 이를 확인하기 위해 get_health() 메서드를 호출했다.
- wolf도 goblin처럼 객체를 만들어서 메서드를 호출할 수 있다.
# 클래스 생성자
- 생성자 메서드 __init__()은 클래스로 인스턴스(객체)를 만들 때 자동으로 가장 먼저 호출되는 메서드이다.
- 생성자 메서드에는 매개변수가 0 ~ n개로 설정할 수 있다.
- 생성자 메서드의 매개변수를 통해 클래스의 속성들을 초기화시킨다.
- 매개변수 self는 클래스로 생성되는 인스턴스(객체) 자신, 즉 goblin 또는 wolf를 지칭하는 특수한 매개변수이다.
▷ 상속(inheritance)과 메서드 오버라이딩
# 상속을 활용하는 이유
- 캐릭터를 키워서 몬스터를 잡고 레벨업을 하고 아이템을 얻는 RPG 게임에서 몬스터의 종류(땅 몬스터, 물 몬스터, 공중 몬스터,...)가 여러 개 있다고 했을 때,
- 각각의 몬스터에 대해 속성과 메서드를 지정하는 코드를 만든다면 엄청난 코드를 작성해야 하고, 코드에 문제가 발생했을 때 유지보수를 위한 많은 노력과 시간이 들게 된다.
- 이러한 문제점을 해결하는 방법으로 이 몬스터들의 공통적인 특징을 하나의 클래스로 만들어서 사용하면 중복된 코드를 제거할 수 있다. 즉, 상속은 클래스들의 중복된 코드를 제거(코드의 효율적 재사용성 제고)하고 유지보수를 편리하게 할 수 있다.
- 상속을 위해 부모 클래스와 자식 클래스를 만든다.
## 부모 클래스 정의
class Parent_Monster:
def __init__(self, name, health, attack):
self.name = name
self.health = health
self.attack = attack
def move(self):
print(f'{[self.name]} 지상에서 이동하기')
## 자식 클래스 정의
class Dragon(Parent_Monster):
def move(self):
print(f'{[self.name]} 날기')
class Shark(Parent_Monster):
def move(self):
print(f'{[self.name]} 헤엄치기')
class Wolf(Parent_Monster):
pass
dragon = Dragon('드래곤', 1000, 300)
dragon.move()
shark = Shark('샤크', 1500, 400)
shark.move()
wolf = Wolf('울프', 1200, 200)
wolf.move()
[실행 결과]
['드래곤'] 날기
['샤크'] 헤엄치기
['울프'] 지상에서 이동하기
- Parent_Monster 클래스의 속성과 메서드 전체를 Dragon, Shark, Wolf 클래스가 상속을 받기 때문에 Parent_Monster의 속성인 name, health, attack을 Dragon, Shark, Wolf에서 그대로 사용할 수 있고,
- Dragon과 Shark의 이동은 다르기 때문에 Parent_Monster의 move() 메서드를 각 자식 클래스에서 다시 만들어 사용할 수 있다. 이를 메서드 오버라이딩(메서드 재정의)라고 한다.
▷ 생성자(__init__()) 오버라이딩, 클래스 변수
# 생성자 오버라이딩(메서드 오버라이딩)
- 드래곤의 스킬 향상을 위해 자식 클래스인 드래곤 클래스의 속성(인스턴스 속성)에 스킬 3개 추가하되, 드래곤이 스킬을 쓰면 속성 중에 무작위로 하나가 사용되도록 한다.
- 스킬 속성을 주기 위해 드래곤 생성자 활용함 (부모 생성자 오버라이딩 포함)
# 클래스 변수(클래스 자체의 속성)
- 클래스 자체의 속성 변수이며, 인스턴스(객체)들이 모두 공유하는 변수임
- 부모 클래스에서 사용하는 이유 : 몬스터가 너무 많으면 서버에 부하가 될 수 있으니 몬스터 개수에 제한을 걸기 위해 사용하는 변수
import random
## 부모 클래스 정의
class Parent_Monster:
monster_max_num = 1000
def __init__(self, name, health, attack):
self.name = name
self.health = health
self.attack = attack
self.get_monster_count()
Parent_Monster.monster_max_num -= 1
print(f'몬스터 생성수: {Parent_Monster.monster_max_num}')
def move(self):
print(f'{[self.name]} 지상에서 이동하기')
def get_monster_count(self):
if Parent_Monster.monster_max_num == 0:
print('더이상 몬스터를 만들 수 없습니다!!')
## 자식 클래스 정의
class Dragon(Parent_Monster):
# 부모 생성자 오버라이딩
def __init__(self, name, health, attack, skills):
super().__init__(name, health, attack) # 부모 생성자 호출
self.skills = skills[random.randint(0, len(skills)-1)]
def move(self):
print(f'{[self.name]} 날기')
def skill(self):
print(f'{[self.name]} 스킬 사용 - {self.skills}')
class Shark(Parent_Monster):
def move(self):
print(f'{[self.name]} 헤엄치기')
class Wolf(Parent_Monster):
pass
dragon = Dragon('드래곤', 1000, 300, ('불뿜기', '꼬리치기', '날개치기'))
# 호출할 때 스킬을 입력할 수 있음
dragon.move()
dragon.skill()
shark = Shark('샤크', 1500, 400)
shark.move()
wolf = Wolf('울프', 1200, 200)
wolf.move()
[실행 결과]
몬스터 생성수: 999
['드래곤'] 날기
['드래곤'] 스킬 사용 - 날개치기
몬스터 생성수: 998
['샤크'] 헤엄치기
몬스터 생성수: 997
['울프'] 지상에서 이동하기
- 클래스 변수에 접근할 때는 '클래스명.클래스변수명'으로 접근함(변수가 '__변수명'으로 선언하면 private 속성 변수)
- 이 코드에서는 변수가 private 속성이 아니기 때문에 객체에서도 접근이 가능하다.(print(wolf.monster_max_num) 등으로 접근 가능)
- 부모 생성자 오버라이딩 부분을 보면, 자식의 생성자에서 부모 생성자를 호출할 때 매개변수가 기본적으로 들어가고 skills라는 매개변수도 들어간다. 이 skills가 dragon만의 스킬 리스트가 되며, random.randint() 함수로 임의 지정이 되도록 했다.
- dragon은 shark, wolf가 없는 skill() 메서드가 있고 이를 사용할 수 있다.
- 몬스터의 생성 수를 체크하기 위해 부모 클래스에 클래스 변수(monster_max_num)를 삽입하고, 이를 고지하기 위해 생성자 메서드에서 출력하도록 했다.
- 부모 클래스에 추가한 get_monster_count() 메서드는 메인 함수에서 몬스터 수를 확인한 후 프로그램을 종료할 때 출력하는 용도로 사용할 수 있다. (메인 함수에서 이 기능을 작성해도 됨)
결론적으로, 클래스를 만들어서 사용하면, 몬스터를 수없이 쉽게 만들 수 있고 몬스터마다 특징을 줄 수 있다.
[관련 포스팅 더보기]
파이썬 클래스 연습 - 게임 아이템의 종류 구입, 사용, 버리기 메서드
'코딩 연습' 카테고리의 다른 글
파이썬 os 모듈(os 또는 os.path)과 pathlib 모듈 비교 (0) | 2022.06.06 |
---|---|
파이썬 클래스 연습 - 게임 아이템의 종류 구입, 사용, 버리기 메서드 (0) | 2022.06.05 |
엑셀 파일 병합하기 - openpyxl 활용 (0) | 2022.06.01 |
python 가상 환경 오류 - 'activate' 용어가 cmdlet, 함수,... 조치 방법 (0) | 2022.05.26 |
내 아이피 찾아보기 [파이썬으로 공인 ip & 내부 ip 검색] (0) | 2022.05.12 |
댓글