이 게시글은
Fastcampus
에서 제공하는한 번에 끝내는 파이썬 웹 개발 초격차 패키지 Online.
강의를 듣고 작성하였습니다.
기능들을 어떤
순서
로 처리할 것인가에 초점을 맞춘다.
- 씻기
- 옷 갈아입기
- 밥 먹기
📌 이와 같이 순서가 중심이 되어 프로그래밍
하는 방식
객체가 중심이 되고, 객체를 정의하고 객체간 상호작용에 초점을 맞춘다.
- A라는 사람이 있고 B라는 사람이 있음
- A, B는 사람이자 객체
- A가 B에게 돈을 빌려주는 행동을 하면
- A는 -10 만큼 재산이 깎이고 B는 10이 재산이 늘어남
📌 이와 같이 객체간에 서로 어떤 행동에 의해 상호작용
이 일어나는 것을 객체 지향 프로그래밍이라고 함
프로그램의 규모
에 따라 다름
절차 지향
객체 지향
방식이 효율적클래스
📌 객체를 만들기 위한 설계도
객체
📌 설계도로부터 만들어낸 제품
클래스 만들기
class 클래스이름:
# pass : 아직 구현하지 않고 넘어가고, 나중에 구현하겠다는 것을 의미
pass
객체 만들기
인스턴스 = 클래스이름()
속성 추가하기
class Unit:
# __init__ 메서드 : 생성자
# 생성자의 첫 번째 변수로는 반드시 self가 반드시 들어감
# self는 객체 자기 자신을 의미
# 두번째 변수부터는 속성값으로 사용할 데이터를 입력받는 역할을 하게 됨.
def __init__(self, name, hp, shield, demage):
# 클래스의 속성 : self.속성명 형태로 작성하고, 위에서 입력받을 변수를 할당하는 형태로 작성
self.name = name # 이름
self.hp = hp # 체력
self.shield = shield # 방어막
self.demage = demage # 공격력
실제 객체 생성하기
probe = Unit("프로브", 20, 20, 5)
zealot = Unit("질럿", 100, 60, 16)
dragoon = Unit("드라군", 100, 80, 20)
메서드 추가하기
class Unit:
def __init__(self, name, hp, shield, demage):
self.name = name
self.hp = hp
self.shield = shield
self.demage = demage
# __str__ 메서드
def __str__(self):
return f"[{self.name}] 체력 : {self.hp} 실드 : {self.shield} 공격력 : {self.demage}"
객체 생성
probe = Unit("프로브", 20, 20, 5)
# print 실행 시, __str__ 메서드가 실행되게 됨.
print(probe)
# 프로브 체력 : 20 실드 : 20 공격력 : 5
객체마다 다르게 가지는 속성
class Unit:
def __init__(self, name, hp, shield, demage):
self.name = name # 인스턴스 속성
self.hp = hp # 인스턴스 속성
self.shield = shield # 인스턴스 속성
self.demage = demage # 인스턴스 속성
인스턴스 속성 사용 방법
self.속성명
: 클래스 안에서 인스턴스를 사용할 때객체명.속성명
: 클래스 밖에서 인스턴스를 사용할 때
모든 객체가 공유하는 속성
class Unit:
count = 0 # 클래스 속성
def __init__(self, name, hp, shield, demage):
self.name = name
self.hp = hp
self.shield = shield
self.demage = demage
Unit.count += 1 # 클래스 속성
클래스 안에서만 접근 가능한 속성
📌 클래스 밖에서 속성에 접근하려고 하면 접근 자체가 불가한 속성
class Unit:
def __init__(self, name, hp, shield, demage):
self.name = name
self.__hp = hp # 비공개 속성
self.shield = shield
self.demage = demage
📌 실습 코드
클래스 생성
class Unit:
count = 0
def __init__(self, name, hp, shield, demage):
self.name = name
self.__hp = hp
self.shield = shield
self.demage = demage
Unit.count += 1
print(f"[{self.name} (이)가 생성 되었습니다.")
def __str__(self):
return f"[{self.name}] 체력 : {self.__hp} 실드 : {self.shield} 공격력 : {self.demage}"
객체 생성
# 객체 3개 생성
probe = Unit("프로브", 20, 20, 5)
zealot = Unit("질럿", 100, 60, 16)
dragoon = Unit("드라군", 100, 80, 20)
출력을 통한 확인
# 인스턴스 속성
print(probe.name) # 프로브
print(zealot.hp) # 9999
print(dragoon.damage) # 20
# 클래스 속성
print(Unit.count)
# 비공개 속성 접근
prove.__hp = 9999
print(prove) # [프로브] 체력 : 20 방어막 : 20 공격력 : 6
# 변경되지 않음.
📌 네임 맹글링 (name mangling)
- 이름을 뭉개서 해당 속성을 접근할 수 없게 만든다는 뜻
- 그러나 아래와 같이 접근하면 수정 가능
probe._Unit__hp = 9999
print(prove) # [프로브] 체력 : 9999 방어막 : 20 공격력 : 6
인스턴스 속성에 접근할 수 있는 메서드.
📌 항상 첫번째 파라미터로 self를 갖는다.
hit 메서드 구현하기
데미지
를 받으면 체력
과 방어막
이 깎이는 **hit 메서드
**를 구현해 보자.데미지
가 방어막
보다 작거나 같으면 방어막
만 깎인다.
데미지
가 방어막
보다 크고 체력
보다 작으면 체력
과 방어막
이 깎인다.
데미지
가 체력
보다 크면 체력
을 0
으로 만든다.
인스턴스 메서드
<class Unit:
count = 0
def __init__(self, name, hp, shield, demage):
self.name = name
self.__hp = hp
self.shield = shield
self.demage = demage
Unit.count += 1
print(f"[{self.name} (이)가 생성 되었습니다.")
def __str__(self):
return f"[{self.name}] 체력 : {self.__hp} 실드 : {self.shield} 공격력 : {self.demage}"
def hit(self, damage):
# 방어막 변경
if self.shield >= damage:
self.shield -= damage
damage = 0
elif:
damage -= shelf.shield
self.shield = 0
# 체력 변경
if damage > 0:
if self.hp > damage:
self.hp -= damage
else:
self.hp -= 0
클래스 속성에 접근하기 위해서 사용
한다.
📌 클래스를 의미하는 cls
를 파라미터로 받는다.
📌 실습 코드
클래스 메서드
class Unit:
count = 0
...
@classmethod # 데코레이터 형태로 사용
def print_count(cls):
print(f"전체 유닛 개수 : {cls.count}")
#클래스 메서드를 실행할 때는 객체를 거칠 필요 없이 바로 클래스에서 바로 호출 가능
Unit.print_count() # 생성된 유닛 개수 : 3개
인스턴스를 만들 필요가 없는 메서드
📌 self를 받지 않는다.
📌 실습 코드
정적 메서드
Math라는 클래스를 만들고 staticmethod 클래스를 math클래스 안에 구현
class Math:
@staticmethod
def add(x, y):
return x + y
@staticmethod
def sub(x, y):
return x - y
print(Math.add(3, 4)) # 7
print(Math.sub(3, 4)) # -1
클래스 안에서 정의가 가능한 스페셜 메서드
📌 특별한 상황에 호출된다.
__이름__
의 형태로 되어있다.__init__
메서드 : 클래스에서 객체를 생성할 때 호출되는 메서드__str__
메서드 : 클래스에서 객체를 출력할 때 호출되는 메서드
📌 dir
: 객체가 가지고 있는 메서드와 속성을 확인할 수 있는 함수
- dir(prove) : prove 객체가 가지고 있는 메서드와 속성을 확인
📌 클래스들의 공통된 속성과 메서드
를 뽑아내서 부모 클래스를 만든다.
이를 자식 클래스에서 상속받아서 사용한다.
📌 코드의 중복을 제거 가능
- 기존에는 여러가지 자식 클래스에서 각각 공통된, 중복된 코드를 일일히 만들어야 했지만,
부모 클래스에 공통된 코드를 만들어 놓으면 자식 클래스에서는 그대로 받아서 사용하면 됨.
📌 간편한 유지보수
부모클래스
에공통된 코드
를 작성해놓으면 그 코드를 수정하면 되고,
자식클래스 각각
에 구현한상세 코드
는 그 코드만 수정하면 되기 때문
상속 실습
파트에서 코드 작성 예정
추상클래스
는추상메서드
를 가질 수 있음
추상메서드
: 상속받는 자식 클래스에서 구현을 강제하도록 만드는 것추상클래스
: 추상메서드를 하나라도 가지고 있는 클래스
📌 만약, 추상메서드를 만들었는데 자식클래스에서 해당 메서드를 구현하지 않으면 아래와 같은 오류 메세지 발생
'Item' Class 생성
class Item:
"""
속성 : 이름
메서드 : 줍기, 버리기
"""
def __init__(self, name):
self.name = name
def pick(self):
print(f"[{self.name}] 을(를) 주웠습니다.")
def discard(self):
print(f"[{self.name}] 을(를) 버렸습니다.")
'Weapon' Class 생성
class Weapon(Item):
"""
속성 : 공격력
메서드 : 공격하기
"""
def __init(self, name, damage):
# super() : 부모 클래스를 호출
# 거기에 생성자를 호출해서 이름을 넘겨주겠다!!
super().__init__(name)
self.damage = damage
def attack(self):
print(f"[{self.name}] 을(를) 이용해 {self.damage} 로(으로) 공격합니다.")
'HealingItem' Class 생성
class HealingItem(Item):
"""
속성 : 회복량
메서드 : 사용하기
"""
def __init__(self, name, recovery_amount):
super().__init(name)
self.recovery_amount = recovery_amount
def use(self):
print(f"[{self.name}] 을(를) 사용합니다. {self.recovery_amount} 회복")
객체 생성
# 객체 생성
m16 = Weapon("m16", 110)
bungdae = HealingItem("붕대", 20)
m16.attack()
# m16 을(를) 이용해 110 로(으로) 공격합니다.
bungdae.use()
# bungdae 을(를) 사용합니다. 20 회복
추상클래스
실습
from abc import *
# 'ABCMeta' 라는 'metaclasss'를 상속받으면 Item 클래스가 추상클래스가 됨.
# 추상클래스로 만들고 나면 안에서 추상메서드를 사용할 수 있게 됨.
class Item(metaclass=ABCMeta):
"""
속성 : 이름
메서드 : 줍기, 버리기
"""
def __init__(self, name):
self.name = name
def pick(self):
print(f"[{self.name}] 을(를) 주웠습니다.")
def discard(self):
print(f"[{self.name}] 을(를) 버렸습니다.")
# 추상메서드 만들때, abstractmethod 라는 메서드를 데코레이터로 사용
# use 라는 메서드를 구현하지 않고 자식클래스로 넘김
@abstracmethod
def use(self):
pass
class Weapon(Item):
"""
속성 : 공격력
메서드 : 공격하기
"""
def __init(self, name, damage):
# super() : 부모 클래스를 호출
# 거기에 생성자를 호출해서 이름을 넘겨주겠다!!
super().__init__(name)
self.damage = damage
def use(self):
print(f"[{self.name}] 을(를) 이용해 {self.damage} 로(으로) 공격합니다.")
class HealingItem(Item):
"""
속성 : 회복량
메서드 : 사용하기
"""
def __init__(self, name, recovery_amount):
super().__init__(name)
self.recovery_amount = recovery_amount
def use(self):
print(f"[{self.name}] 을(를) 사용합니다. {self.recovery_amount} 회복")
# 객체 생성
m16 = Weapon("m16", 110)
bungdae = HealingItem("붕대", 20)
m16.use()
# m16 을(를) 이용해 110 로(으로) 공격합니다.
bungdae.use()
# bungdae 을(를) 사용합니다. 20 회복
📌 is-a
관계: 서로 상속을 할 때 발생하는 관계
- Weapon
is-a
ItemWeapon < Item
📌 has-a
관계: 어떤 특정 클래스가 다른 클래스의 객체를 가지고 있는 관계. 1:N 관계
라고도 정리 가능.
- Player
has-a
Unit
Player
하나의 객체가 여러개의Unit
객체를 가지고 있음.
속성
: 닉네임, 미네랄, 가스, 유닛리스트
메서드
: 생산하기
produce(이름, 미네랄, 가스, 체력, 방어막, 공격력)
Player
의 미네랄과 가스가 충분한 경우 :유닛 객체를 생성
하고유닛리스트에 추가
한다.Player
의 미네랄이 부족한 경우 :"미네랄이 부족합니다"
를 출력Player
의 가스가 부족한 경우 :"가스가 부족합니다"
를 출력
# 유닛 정보
unit_info = {
"probe" : {
"name" : "프로브",
"mineral" : 50,
"gas" : 0,
"hp" : 20,
"shield" : 20,
"demage" : 5
},
"zealot" : {
"name" : "질럿",
"mineral" : 100,
"gas" : 0,
"hp" : 100,
"shield" : 60,
"demage" : 16
},
"dragoon" : {
"name" : "드라군",
"mineral" : 125,
"gas" : 50,
"hp" : 100,
"shield" : 80,
"demage" : 20
}
}
# -----------
# 클래스 생성
class Unit(Player):
"""
속성 : 이름, 체력, 방어막, 공격력
"""
def __init(self. hp, shield, damage):
self.hp = hp
self.shield = shield
self.damage = damage
class Player:
"""
속성 : 닉네임, 미네랄, 가스, 유닛리스트
메서드 : 유닛 생산하기
"""
def __init__(self, nickname, mineral, gas, unit_list=[]):
self.nickname = nickname
self.mineral = mineral
self.gas = gas
self.unit_list = unit_list
# 생산하기 메서드
def produce(self, name, mineral, gas, hp, shield, damage):
if self.mineral - mineral < 0:
print("미네랄이 부족합니다.")
elif self.gas - gas < 0:
print("가스가 부족합니다.")
else:
self.mineral -= mineral
self.gas -= gas
unit = Unit(name, hp, shield, damage)
self.unit_list.append(list)
print(f"[{name}] 을(를) 생산합니다.")
객체 생성
# 플레이어 생성 1
# unit_list는 default 값으로 '[]'를 지정해두었기 때문에 인자로 넣지 않아도 빈 리스트로 지정됨.
player1 = Player("Bisu", 400, 10)
# 유닛 생성
player1.produce(unit_info['probe']['name'], unit_info['probe']['mineral'], unit_info['probe']['gas'], unit_info['probe']['hp'], unit_info['probe']['shield'], unit_info['probe']['damage']
# [프로브] 을(를) 생산합니다.
player2.produce(unit_info['zealot']['name'], unit_info['zealot']['mineral'], unit_info['zealot']['gas'], unit_info['zealot']['hp'], unit_info['zealot']['shield'], unit_info['zealot']['damage']
# [질럿] 을(를) 생산합니다.
player3.produce(unit_info['dragoon']['name'], unit_info['dragoon']['mineral'], unit_info['dragoon']['gas'], unit_info['dragoon']['hp'], unit_info['dragoon']['shield'], unit_info['dragoon']['damage']
# 가스가 부족합니다.
# 생성된 모든 유닛 확인
for unit in player1.unit_list:
print(f"[{unit.name}] 체력 : {unit.hp} 방어막 : {unit.shield} 공격력 : {unit.damage}")
# [프로브] 체력 : 20 방어막 : 20 공격력 : 5
# [질럿] 체력 : 100 방어막 : 60 공격력 : 16
# 플레이어 생성 2
# unit_list는 default 값으로 '[]'를 지정해두었기 때문에 인자로 넣지 않아도 빈 리스트로 지정됨.
player1 = Player("Bisu", 500, 200)
# 유닛 생성
player1.produce(unit_info['probe']['name'], unit_info['probe']['mineral'], unit_info['probe']['gas'], unit_info['probe']['hp'], unit_info['probe']['shield'], unit_info['probe']['damage']
# [프로브] 을(를) 생산합니다.
player2.produce(unit_info['zealot']['name'], unit_info['zealot']['mineral'], unit_info['zealot']['gas'], unit_info['zealot']['hp'], unit_info['zealot']['shield'], unit_info['zealot']['damage']
# [질럿] 을(를) 생산합니다.
player3.produce(unit_info['dragoon']['name'], unit_info['dragoon']['mineral'], unit_info['dragoon']['gas'], unit_info['dragoon']['hp'], unit_info['dragoon']['shield'], unit_info['dragoon']['damage']
# [드라군] 을(를) 생산합니다.
# 생성된 모든 유닛 확인
for unit in player1.unit_list:
print(f"[{unit.name}] 체력 : {unit.hp} 방어막 : {unit.shield} 공격력 : {unit.damage}")
# [프로브] 체력 : 20 방어막 : 20 공격력 : 5
# [질럿] 체력 : 100 방어막 : 60 공격력 : 16
# [드라군] 체력 : 100 방어막 : 80 공격력 : 20