오늘 포스팅할 주제인 Class는 전략 시뮬레이션 게임을 해보셨던 분들이라면 이해가 조금 빠를 것이다. 이 클래스라는 개념은 객체를 다루기 위해 사용하는 문법이며, 클래스를 사용하기 위한 예시로 들기 가장 좋은 예는 20년 전에 대유행을 했던 스타크**트라는 게임이다(물론, Age of Empire와 같이 유닛이 나오는 유사 게임도 마찬가지다)
조금, 조악하지만, 텍스트 형태로 스타크**트를 만들어보려 한다고 해보자. 필자는 우선 SCV 4마리(?)를 먼저 생성하려고 한다.
SCV1 = "SCV1"
SCV2 = "SCV2"
SCV3 = "SCV3"
SCV4 = "SCV4"
그리고, 이들 SCV가 이동하거나, 건설하거나, 광물을 채취할 수 있는 등의 행동을 제어할 수 있는 함수를 만들려고 한다.
def move(SCV, coordinate): print("{} is moving to {}".format(SCV, coordinate))
def build(SCV, building): print("{} is building {}".format(SCV, building))
def extract(SCV, resource): print("{} is extracting resource {}".format(SCV, resource))
그리고 각 함수에 SCV 번호와 대상을 넣으면, 각 SCV들이 건물을 짓거나 광물을 채취하는 등의 행위를 할 수 있도록 만들수 있다.
이제 여기에 마린(Marine)을 추가해보자. 아시는 분들은 아시겠지만, 마린의 경우 SCV 처럼 광물 채취나 건물 건설이 불가능한, 순수 전투 유닛이다. 따라서, 마린이 무언가를 공격할 수 있는 행위도 함수로 만들어 추가해줄 것이다.
marine1 = "Marine1"
def rifle_attack(marine, target): print("{} is attacking {}".format(marine, target))
마린에 대한 변수 및 행동 함수를 선언함으로써, 마린1이 SCV1을 공격하는 상황도 만들 수 있다. 그런데...
이런 방식으로 각 유닛들을 생성하는 방법은 한 가지 큰 문제점이 있다. 알다시피, SCV는 마린과 같이 소총을 이용한 공격을 하지 않는다. 하지만, 다음과 같이 코드를 작성하게 되면...
SCV가 마린을 소총으로 공격하는 웃지 못할 상황이 벌어진다... 동일한 방식으로, 마린 역시 SCV와 같이 자원 채취나 건물 건설과 같은 중노동을 시킬 수 있게 된다.
따라서, 유닛 별로, 특정 행동 함수만을 사용할 수 있도록, 그리고 각 유닛에게 부여되는 체력과 같은 변수도 달리 설정해주어야 할 필요가 있다. 이를 위해 사용하는 것이 클래스(Class)다.
1. 클래스 선언 및 호출
클래스는 특정 객체 - 그러니까 위의 스타크래프트로 설명하자면 유닛 -에 대한 신상 명세서를 작성하는 것이라고 보면 된다. SCV는 체력 60, 공격력 4를 기본값으로 가지고, 가능한 행동은 움직임, 멈춤, 공격, 자원채취, 건물 건설이 있다. 이제 이 스팩을 SCV 클래스로 작성해보려 한다.
클래스를 사용하기 위해서는, 함수와 똑같이, 클래스를 사용한다고 선언을 해야한다.
class SCV_Specification():
함수는 def를 입력하여 선언하는 것과 달리, 클래스는 class를 입력하여 선언한다. 클래스를 선언하고 나면, 함수와 마찬가지로 마지막을 콜론(:)으로 마무리한 뒤, 공백(indentation)을 넣어 아랫줄부터 SCV 신상을 작성해주면 된다. 우선 체력과 공격력에 대한 내용을 변수로 지정해보자.
------------------------------------------------------------------------
class SCV_Specification():
def __init__(self):
self.HP = 60
self.attack_damage = 4
------------------------------------------------------------------------
클래스를 선언하면, 클래스 내부에서 사용할 수 있는 특이한 함수가 하나 존재하는데, 그 이름은 __init__(self) 이다. 이 함수는, SCV가 클래스에 의해 생성되면, 생성되는 그 시점에 __init__ 함수 내에 언급한 변수 선언이나 함수 내용을 자동으로 실행하도록 만들어준다. 이를 확인하기 위해, 클래스를 호출하여 SCV를 1기 생산해보겠다. 클래스를 참조하여 객체(유닛)을 만드는 코드는 아래와 같다.
SCV1 = SCV_Specification() => 유닛 변수명 = 호출할 클래스 이름()
그리고 def __init__ 함수를 보면 self라고 적혀있는 인자도 보인다. 이 인자가 의미하는 것은, 클래스 객체가 만들어지면, 그 객체만이 가지는 변수나 함수를 의미한다고 보면 된다. 즉, self.HP라고 정의된 부분은, 새 SCV가 생성되면 그 SCV의 HP를 60으로 만들겠다는 의미다. 클래스를 선언하여 SCV 객체를 하나 만들고 나서 HP를 확인하고 싶다면, self 대신 SCV 객체명(변수명)을 self 자리에 대신 넣어주면 된다.
SCV2나 SCV3를 만들어도 동일한 상황이 벌어지며, 심지어 이 변수명을 Marine1로 바꾸고 SCV 클래스를 사용하면 marine1은 이름만 마린일 뿐, 내용은 SCV가 된다.
2. 클래스 내에 함수 정의하기
이제 SCV의 행동 함수를 SCV_Specification 클래스 내에 정의해보자. 이는 크게 어렵지 않다. 함수 선언 및 호출과 유사한 방식으로 진행하면 된다. 행동 함수를 정의하기 위해 클래스에 조금 변경을 가해본다.
-----------------------------------------------------------------------
class SCV_Specification():
def __init__(self):
self.Name = "SCV"
self.HP = 60
self.attack_damage = 4
def extract(self, resource): print("{} is extracting {}".format(self.Name, resource))
def build(self, building): print("{} is building {}".format(self.Name, building))
def attack(self, unit): print("{} is attacking {}".format(self.Name, unit))
------------------------------------------------------------------------
클래스 내에 정의된 함수를 보다보면, 일반 함수 정의 방식과 다른 점이 눈에 띄는데, 바로 self 인자의 존재이다. 클래스 내에 정의된 함수는 클래스 객체만을 위해 사용한다는 것을 명시하기 위해, 반드시 self 인자를 추가해주어야 한다. 만약 이 self 인자 없이 함수를 정의하면, 해당 클래스의 객체는 함수를 자신의 것으로 사용할 수 없다.
이제, 위의 내용을 보자. SCV 유닛에 대한 객체 스팩을 정의하고, 4기의 SCV를 생성하는 과정을 거쳤다. 그리고 각 SCV 마다 특정 행위를 할 수 있도록 동작 함수를 호출했다. 클래스 내의 함수를 호출하는 방법은 일반 함수 호출과 조금 다른데, 생성한 클래스 객체(SCV1, SCV2, SCV3, SCV4) 뒤에 점(.)을 찍고 함수를 호출하면 된다.
SCV1.extract("Mineral")
이 말은, SCV_Specification 클래스의 객체(개별 유닛)인 SCV1이 자신이 정의된 클래스 내의 함수 extract(resource)를 사용한다는 의미다. 함수와 마찬가지로 변수 또한 호출하는 것이 가능한데, 이는 이미 앞에서 self.HP를 print()함수로 호출하며 확인해보았다.
3. 다른 유닛을 공격해보자.
이제, 마린 유닛에 대한 클래스를 생성한 뒤, 마린 1기 생산 후, SCV 하나에게 공격을 가해보도록 하겠다. 이 때, SCV는 공격을 받으면 마린의 공격력만큼 HP가 손실되어야 한다.
마린에 대한 클래스 정의는 다음과 같이 간단히만 작성한다.
-----------------------------------------------------------------------
class Marine_Specification():
def __init__(self):
self.Name = "Marine"
self.HP = 40
self.attack_damage = 5
def attack(self, unit): # 단, 여기서 unit은 클래스의 객체로 사용한다
print("{} is attacking {}".format(self.Name, unit.Name))
unit.HP -= self.attack_damage
------------------------------------------------------------------------
기존의 SCV의 HP는 60이나, 마린이 한 번 공격하고 나니, HP가 마린의 공격력만큼 떨어진 것이 보인다. 이처럼, 클래스를 이용하면, 유사하지만 서로 다른 객체에게 독립된 영향력만 행사할 수 있다. 다음 예시를 보자.
SCV2와 SCV3를 생성하고, Marine으로 SCV2만 주구장창 때린 결과, SCV2만 HP가 깎여 나간것을 확인할 수 있다.
클래스는 사실 이렇게 객체를 많이 만들어야 하는 상황이 아니라면, 자주 사용하는 문법은 아니다. 그렇기 때문에 클래스 사용에 익숙해지는 것이 쉽지 않은 것도 사실이다. 하지만, 클래스에 익숙해지면 이전에 노가다 형식으로 작성했던 여러 코드들도 조금 더 깔끔하게 작성하는 것이 가능해진다.(사실 이 단계까지 생각하기 위해서는 부단히 코딩 연습을 해야한다. 필자도 이 부분에서는 아직 많이 부족하고...)
다음 포스팅에서는 파이썬 코드 파일을 불러와 그 파일 내에 존재하는 함수를 사용하는, import 기능에 대해 알아보려 한다.
FIN.
'Python > Python Basic' 카테고리의 다른 글
20. Python - 파일 읽기, 쓰기 및 추가 (0) | 2020.05.17 |
---|---|
19. Python - 다른 Python 파일의 함수, 변수를 호출하기 (0) | 2020.05.03 |
17. Python - Python 함수의 입력값(인자) 수를 동적으로 정의하기 (0) | 2020.03.17 |
16. Python - Python 함수의 종류 및 사용 예시 (0) | 2020.03.01 |
15. Python - Python 함수 사용 이유, 선언 및 호출 (0) | 2020.02.07 |
댓글