본문 바로가기
AI 공부/파이썬

파이썬 (클래스 - 2 ) 상속 클래스, 오버라이딩, private, 매직 메소드, getter&setter

by AI Sonny 2022. 8. 4.
728x90

상속 클래스

  • 구현된 클래스의 기능(메소드)을 그대로 가져다가 사용하거나 아니면 그 기능 수정하거나 아니면 추가하거나 할 때 사용하는 개념
  • 부모클래스의 속성(인스턴스 변수와 메소드)들을 자식클래스가 그대로 물려받는 개념
  • 확장 개념, 부모클래스와 자식클래스가 합쳐지는 개념

 

1) 부모클래스 정의

 

class PlayerCharacter:
   def __init__(self,hp=100,exp=0):
       self.hp = hp
       self.exp = exp
    
   def attack(self):
       print("공격하기")
       self.exp += 2

   def defend(self):
      print("방어하기")
      self.exp += 1

 

2) 자식클래스 선언

 

class Wizard(PlayerCharacter):  # 부모 클래스 넣어 주기
    def __init__(self,mp):
        self.mp = mp
        super().__init__()     # 부모클래스의 생성자를 실행하겠다.

    def magic_skill(self):
        print("마법 공격하기")
        self.mp = self.mp - 2

 

상속 클래스의 부모 클래스는 일반 클래스와 동일하나 자식 클래스는 클래스를 선언하고, 자식 클래스를 넣어줘야 한다.

 

그리고 super( ).__init__( )을 사용하면 부모클래스의 생성자를 이용하겠다는 뜻이다.

 

그래서 부모클래스의 생성자를 쓰려면 항상 있어야한다.

 

3) 실행

 

player = Wizard(10)
player.attack()

=> 공격하기

 

증명

 

class PlayerCharacter:
   def __init__(self,hp=100,exp=0):
       print(f"부모의 주소값: {id(self)}")  # 에러 시 사용하여 확인
       self.hp = hp
       self.exp = exp
    
   def attack(self):
       print("공격하기")
       self.exp += 2

   def defend(self):
      print("방어하기")
      self.exp += 1
      
class Wizard(PlayerCharacter):  # 부모 클래스 넣어 주기
    def __init__(self,mp):
        self.mp = mp
        super().__init__()     # 부모클래스의 생성자를 실행하겠다.
        print(f"자식의 주소값: {id(self)}")
    def magic_skill(self):
        print("마법 공격하기")
        self.mp = self.mp - 2
        
        
Wizard(30)

=>	부모의 주소값: 139938407770960
	자식의 주소값: 139938407770960
	<__main__.Wizard at 0x7f45f315eb50>

 

다음과 같이 상속 클래스를 만들고, id를 이용하여 주소값을 출력하면 주소값이 같은 것을 알 수 있다.

 


오버라이딩 (Override)

  • 부모로부터 받는 메소드를 수정하고 싶을 때 자식클래스에서 재정의한다.
  • 다른 자식클래스를 만들어도 똑같이 적용된다.

 

예시

 

class PlayerCharacter:
   def __init__(self,hp=100,exp=0):
       self.hp = hp
       self.exp = exp
    
   def attack(self):
       print("공격하기")
       self.exp += 2

   def defend(self):
      print("방어하기")
      self.exp += 1
      
class Wizard(PlayerCharacter):  # 부모 클래스 넣어 주기
    def __init__(self,mp):
        self.mp = mp
        super().__init__()     # 부모클래스의 생성자를 실행하겠다.
    def magic_skill(self):
        print("마법 공격하기")
        self.mp = self.mp - 2

    def defend(self):          # 메소드 오버라이딩!
        print("마법사가 방어하기")
        self.exp += 3
        
player = Wizard(10)
player.defend()

=> 마법사가 방어하기

 

위와 같이 처음 defend는 부모 클래스에서 선언이 되었지만 자식 클래스에서 defend를 수정하여 출력할 수 있다.

 

여기서 다른 자식 클래스 만들어 선언해도 수정값은 변경되지 않는다.

 


non public (private)

  • private: 인스턴스 변수나 메소드를 클래스 내부에서만 사용하게 하는 것
  • 바깥에서 사용이 불가능하도록 하는 설정
  • 맹글링(mangling) 기법을 이용해서 외부에서 직접적으로 인스턴스 변수나 메소드에 접근하는 것을 막을 수 있다.

 

예시

 

class Wizard(PlayerCharacter):
    def __init__(self,mp):
        super().__init__()
        self.__mp = mp  # 변수도 private

    def __magic_skill(self):    # private
        print("마법 공격하기")
        self.__mp -= 2
    
    def magic_skill(self):
        if self.__mp > 1:
            self.__magic_skill()
        else:
            print("MP가 부족합니다.")

player = Wizard(50)
player.__magic_skill()

=>	AttributeError                            Traceback (most recent call last)
	<ipython-input-251-4ad15eaae4fe> in <module>()
	      1 player = Wizard(50)
	----> 2 player.__magic_skill()
	
	AttributeError: 'Wizard' object has no attribute '__magic_skill'

 

private는 간단하게 접근을 막고 싶은 메소드 앞에 "__" 을 붙여 넣으면 된다. 

 

그래서 위 코드를 보면 "__magic_skill"를 써서 private 하게 만들어 주었기 때문에 접근할 때 에러가 나타나게 된다.

 


매직 메소드

  • 메소드 명이 두개의 언더바로 감싸져있다.
  • 파이썬의 다양한 내장함수들이 클래스의 매직메소드들을 호출하여 결과를 만들어 낸다.
  • call, str, len, getitem 이 있다.

예시

 

class MyDataset:
    def __init__(self,data):    
        self.data = data
    
    def __call__(self,a):
        print(f"{a} 함수 호출 방법처럼 객체를 함수 호출하듯이 만들어 주는 메소드")

    def __str__(self):  # print 함수에 이 클래스의 객체를 넣을 경우, 이 메소드에 리턴값을 출력해준다.
        return "My dataset Class"

    def __len__(self):  # len() 내장함수는 객체의 이 매직메소드를 호출!
        return len(self.data)
    
    def __getitem__(self,idx):  # 인덱싱과 슬라이싱을 가능하게 한다.
        return self.data[idx]

 

1) 50 ~ 99까지의 리스트를 만들었다.

 

이것들을 매직 메소드에 적용시켜보겠다.

 

data = list(range(50,100))
dt = MyDataset(data)
dt

 

2) 실행

 

dt(1004)	# __call__

=> 1004 함수 호출 방법처럼 객체를 함수 호출하듯이 만들어 주는 메소드

------------------------------------------------------------------

print(dt)	# __str__

=> My dataset Class

------------------------------------------------------------------

len(dt)		# __len__

=> 50

------------------------------------------------------------------

dt[:0]  # get_item	# __getitem__

=> 50

------------------------------------------------------------------

 

위와 같이 매직 함수를 이용함으로써 코딩의 질을 높일 수 있다.

 


getter&setter (참고용)

  • 인스턴스 변수에 접근할 때 특정 로직을 거쳐서 접근시키는 방법
  • getter & setter 정의할 인스턴스 변수는 private화 하자.

 

class Wizard(PlayerCharacter):

    def __init__(self,mp):
        super().__init__()
        self.mp = mp

    @property   # getter 세팅할 때는 다음과 같이 데코 넣기
    def mp(self):
        print("getter 동작")
        return self.__mp

    @mp.setter  # setter 세팅할 때는 getter의 메소드명.setter 데코
    def mp(self,mp):
        print("setter 동작")
        if mp < 0:
            mp = 0

        self.__mp = mp

getter랑 setter는 그냥 이런 것이 있구나 한번 이해하고, 지나가면 된다고 해서 그리 깊게 보진 않았다.

 


 

오늘도 문제를 많이 풀었는데 많이 부족하다는 걸 느낀다.

 

문제를 푸는 것이 제일 힘들고, 하기 싫지만 이것만큼 실력이 많이느는 것이 없는 것 같다.

 

하기 싫다는 생각이 들땐 이것 또한 자의식의 방해라 생각하고, 열심히 해야겠다!

 

728x90

댓글