221016_[Python] Class

Csw·2022년 10월 16일
0

Python

목록 보기
1/1

이 게시글은 Fastcampus에서 제공하는 한 번에 끝내는 파이썬 웹 개발 초격차 패키지 Online. 강의를 듣고 작성하였습니다.

🚀 CH04_01. 클래스와 객체

🏳‍🌈 목차

  1. 클래스와 객체
  2. 여러가지 속성 (인스턴스 속성, 클래스 속성, 비공개 속성)
  3. 여러가지 메서드 (인스턴스 메서드, 클래스 메서드, 정적 메서드, 매직 메서드)
  4. 상속 (오버 라이딩, 추상클래스)

🏳‍🌈 절차 지향 vs 객체 지향


🚧 절차 지향 프로그래밍

기능들을 어떤 순서로 처리할 것인가에 초점을 맞춘다.

  • 예를 들어, 아침에 일어나면
    1. 씻기
    2. 옷 갈아입기
    3. 밥 먹기

📌 이와 같이 순서가 중심이 되어 프로그래밍 하는 방식

🚧 객체 지향 프로그래밍

객체가 중심이 되고, 객체를 정의하고 객체간 상호작용에 초점을 맞춘다.

  • 예를 들어,
    • 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)

메서드 추가하기

  • Unit 클래스에 정보를 출력하는 메서드를 추가해 봅시다.
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

🚀 CH04_02. 여러가지 속성


🏳‍🌈 인스턴스 속성 (instance attribute)

객체마다 다르게 가지는 속성

class Unit:
    def __init__(self, name, hp, shield, demage):
        self.name = name          # 인스턴스 속성
        self.hp = hp              # 인스턴스 속성
        self.shield = shield      # 인스턴스 속성
        self.demage = demage      # 인스턴스 속성

인스턴스 속성 사용 방법

  • self.속성명 : 클래스 안에서 인스턴스를 사용할 때
  • 객체명.속성명 : 클래스 밖에서 인스턴스를 사용할 때

🏳‍🌈 클래스 속성 (class attribute)

모든 객체가 공유하는 속성

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          # 클래스 속성

🏳‍🌈 비공개 속성 (private attribute)

클래스 안에서만 접근 가능한 속성

📌 클래스 밖에서 속성에 접근하려고 하면 접근 자체가 불가한 속성

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

🚀 CH04_03. 여러가지 메서드


🏳‍🌈 인스턴스 메서드 (instance method)

인스턴스 속성에 접근할 수 있는 메서드.

📌 항상 첫번째 파라미터로 self를 갖는다.

hit 메서드 구현하기

  • 데미지를 받으면 체력방어막이 깎이는 **hit 메서드**를 구현해 보자.
  1. 데미지방어막보다 작거나 같으면 방어막만 깎인다.

  2. 데미지방어막보다 크고 체력보다 작으면 체력방어막이 깎인다.

  3. 데미지체력보다 크면 체력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

🏳‍🌈 클래스 메서드 (class method)

클래스 속성에 접근하기 위해서 사용한다.

📌 클래스를 의미하는 cls를 파라미터로 받는다.


📌 실습 코드

클래스 메서드

class Unit:
    count = 0
    ...
    @classmethod      # 데코레이터 형태로 사용
    def print_count(cls):
        print(f"전체 유닛 개수 : {cls.count}")

#클래스 메서드를 실행할 때는 객체를 거칠 필요 없이 바로 클래스에서 바로 호출 가능
Unit.print_count()         # 생성된 유닛 개수 : 3개

🏳‍🌈 정적 메서드 (static method)

인스턴스를 만들 필요가 없는 메서드

📌 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

🏳‍🌈 매직 메서드 (magic method)

클래스 안에서 정의가 가능한 스페셜 메서드

📌 특별한 상황에 호출된다.

  • __이름__ 의 형태로 되어있다.
  • __init__ 메서드 : 클래스에서 객체를 생성할 때 호출되는 메서드
  • __str__ 메서드 : 클래스에서 객체를 출력할 때 호출되는 메서드

📌 dir : 객체가 가지고 있는 메서드와 속성을 확인할 수 있는 함수

  • dir(prove) : prove 객체가 가지고 있는 메서드와 속성을 확인

🚀 CH04_04. 상속


🏳‍🌈 상속의 개념

📌 클래스들의 공통된 속성과 메서드를 뽑아내서 부모 클래스를 만든다.

이를 자식 클래스에서 상속받아서 사용한다.


🏳‍🌈 상속의 장점

📌 코드의 중복을 제거 가능

  • 기존에는 여러가지 자식 클래스에서 각각 공통된, 중복된 코드를 일일히 만들어야 했지만,
    부모 클래스에 공통된 코드를 만들어 놓으면 자식 클래스에서는 그대로 받아서 사용하면 됨.

📌 간편한 유지보수

  • 부모클래스공통된 코드를 작성해놓으면 그 코드를 수정하면 되고,
    자식클래스 각각에 구현한 상세 코드는 그 코드만 수정하면 되기 때문

🏳‍🌈 상속 구현하기

상속 실습 파트에서 코드 작성 예정

🏳‍🌈 추상 클래스

추상클래스추상메서드를 가질 수 있음

  • 추상메서드 : 상속받는 자식 클래스에서 구현을 강제하도록 만드는 것
  • 추상클래스 : 추상메서드를 하나라도 가지고 있는 클래스

📌 만약, 추상메서드를 만들었는데 자식클래스에서 해당 메서드를 구현하지 않으면 아래와 같은 오류 메세지 발생

🏳‍🌈 상속 실습

'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 회복

🚀 CH04_05 클래스 실습 문제


🏳‍🌈 상속 관계와 포함 관계

📌 is-a 관계: 서로 상속을 할 때 발생하는 관계

  • Weapon is-a Item

    Weapon < Item

📌 has-a 관계: 어떤 특정 클래스가 다른 클래스의 객체를 가지고 있는 관계. 1:N 관계라고도 정리 가능.

  • Player has-a Unit

    Player 하나의 객체가 여러개의 Unit 객체를 가지고 있음.


🏳‍🌈 Unit, Player 클래스 구현

  1. 속성 : 닉네임, 미네랄, 가스, 유닛리스트

  2. 메서드 : 생산하기
    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

0개의 댓글