파이썬 python

06 클래스

ynzify 2023. 5. 11. 01:35

클래스

  • 변수와 함수를 묶어 놓은 개념
  • 클래스는 객체 (데이터와 가능을 가지고 있는)를 만들기 위한 설계도이다
  • 클래스를 메모리에 객체화 하면 그걸 인스턴스 (instance)라고 한다

 

 

클래스의 구조

  • 변수
    • 인스턴스 변수 (클래스가 인스턴스화 되면 참조 가능한 변수)
    • 클래스 변수 (클래스 정의시에도 참조 가능한 변수) 
  • 함수
    • 메서드

 

 

클래스를 정의하는 방법

  • 클래스 이름 upper camel case (pascal case)로 명명한다
  • class <ClassName>:
        def __init__(self): # 보통 인스턴스 변수들을 초기화할 때 정의한다.
            self.a = 10
            ...
        def <method_name>(self):

 

>>> class PlayerCharacter:
>>>     def __init__(self,hp,exp):
>>>         self.hp = hp # 체력
>>>         self.exp = exp # 경험치

>>>     def attack(self):
>>>         print("공격하기")
>>>         self.exp += 2

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

>>>     def attacked(self,attack_size):
>>>         print("공격받음")
>>>         self.hp -= attack_size

 

>>> class PlayerCharacter:
>>>     def __init__(self,hp,exp):
>>>         # 인스턴스 변수를 선언
>>>         self.hp = hp 
>>>         self.exp = exp
        
>>> player1 = PlayerCharacter(100, 0)
>>> player2 = PlayerCharacter(200,50)

>>> player1.hp, player2.hp
(100, 200)

 

self

  • 클래스가 객체화 되었을 때 자기 자신의 주소를 받는 파라미터
  • 인스턴스화 되었을 때 메모리 상에 어디에 위치해 있는지를 참조하여 인스턴스에 접근 후 그 안에 인스턴스 변수와 메서드를 사용할 수 있다
  • 메서드를 정의할 때는 무조건 첫번째 파라미터에 self를 정의
  • 클래스를 객체화 했을 때 메서드를 사용할 때는 self를 아규먼트로 저장하지 않아도 자동으로 들어간다

 

클래스 변수

  • 클래스 정의 후에 참조 가능한 변수
>>> class PlayerCharacter:
>>>     character_type = "Wizard" # 클래스 변수

>>>     def __init__(self,hp=100,exp=0):
        # 인스턴스 변수
>>>         self.hp = hp
>>>         self.exp = exp
        
>>> PlayerCharacter.hp

 

>>> PlayerCharacter.character_type
'Wizard'

>>> player1 = PlayerCharacter()
>>> player2 = PlayerCharacter(200,20)

>>> player1.character_type, player2.character_type
('Wizard', 'Wizard')

 

클래스 메서드

 

>>> class PlayerCharacter:
>>>     character_type = "Wizard" # 클래스 변수

>>>     @classmethod
>>>     def print_character_type(cls):
>>>         print(cls.character_type)
        
>>> PlayerCharacter.print_character_type()
Wizard

>>> class CFG:
>>>     train_dataset_path = "/data/train/"
>>>     test_dataset_path = "/data/test/"
>>>     model_name = "deep_model"
    
>>> CFG.test_dataset_path
'/data/test/'

 

>>> def preprocessing(cfg):
>>>     print(cfg.train_dataset_path)
>>>     print(cfg.test_dataset_path)
>>>     print(cfg.model_name)

>>> preprocessing(CFG)

/data/train/
/data/test/
deep_model

 

여긴 답이 없어... 맨 땅에 해딩으로 해봐야해 따흑...

 

 

Car 클래스에 차종, 색깔, 차번호를 받아 인스턴스 변수를 초기화하는 클래스를 만들어 주세요.

 

>>> class Car:
>>>     def __init__(self,name,color,num):
>>>         self.name = name
>>>         self.color = color
>>>         self.num = num

>>>     def info(self):
>>>         print(f"차종: {self.name}, 색깔: {self.color}, 차번호: {self.num}")

>>> morning = Car("모닝","Red",1004)
>>> morning.info()

 

 

 

Car 클래스에 차종, 색깔, 차번호를 받아 인스턴스 변수를 초기화하는 클래스를 만들어 주세요.

 

>>> class Car:
>>>     def __init__(self,name,color,num):
>>>         self.name = name
>>>         self.color = color
>>>         self.num = num
        
>>> morning = Car("모닝","Blue",1234)
>>> print(morning.name)
>>> print(morning.color)
>>> print(morning.num)        

모닝
Blue
1234
Car 클래스에 차종, 색깔, 차번호를 모두 출력하는 info라는 메소드를 만들어 보세요.

 

>>> class Car:
>>>     def __init__(self,name,color,num):
>>>         self.name = name
>>>         self.color = color
>>>         self.num = num

>>>     def info(self):
>>>         print(f"차종: {self.name}, 색깔: {self.color}, 차번호: {self.num}")

>>> morning = Car("모닝","Red",1004)
>>> morning.info()

차종: 모닝, 색깔: Red, 차번호: 1004

 

상속

  • 구현된 클래스의 기능을 가져다가 그 기능을 수정하거나 추가할 때 사용하는 개념
  • 부모 클래스의 기능과 속성을 자식 클래스가 그대로 물려받음
  • 부모 클래스랑 자식 클래스가 합쳐진다는 개념

 

>>> 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
>>>         # python 2.x 방식
>>>         # super(Wizard,self).__init__()
>>>         super().__init__() # 부모클래스 __init__ 실행

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

 

>>> mp = 50
>>> player = Wizard(mp)

>>> player.attack()
>>> player.defend()
>>> player.magic_skill()

>>> player.hp, player.exp, player.mp

공격하기
방어하기
마법 공격하기

(100, 3, 48)

 

>>> 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):
>>>         print(f"자식 객체 주소값은: {id(self)}")
>>>         self.mp = mp
        # python 2.x 방식
        # super(Wizard,self).__init__()
>>>         super().__init__() # 부모클래스 __init__ 실행

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

>>> player = Wizard(10)

자식 객체 주소값은: 2737076424128
부모 객체 주소값은: 2737076424128

 

>>> class PlayerCharacter:
>>>     def __init__(self,hp,exp):
>>>         self.hp = hp
>>>         self.exp = exp

>>>     def attack(self):
>>>         print("공격하기")
>>>         self.exp += 2

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

>>>     def attacked(self,attack_size):
>>>         print("공격받음")
>>>         self.hp -= attack_size

 

>>> class Wizard(PlayerCharacter):

>>>     def __init__(self,mp,hp,exp):
>>>         super().__init__(hp,exp)
>>>         self.mp = mp

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

 

>>> mp = 50
>>> hp = 150
>>> exp = 100
>>> player = Wizard(mp,hp,exp)
>>> player.magic_skill()

>>> player.hp, player.exp, player.mp
마법 공격하기
(150, 110, 48)

>>> player.attack()
공격하기

 

오버라이딩 (over riding)

  • 부모 클래스로 부터 받은 메소드를 수정하고 싶을 때 사용
  • 자식 클래스에서 부모 클래스로 부터 받은 메소드를 재정의

 

>>> 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):
>>>         super().__init__()
>>>         self.mp = mp

>>>     def magic_skill(self):
>>>         print("마법 공격하기")

    # 오버라이딩
>>>     def defend(self):
>>>         print("방어하기")
>>>         self.exp += 3

 

>>> player = Wizard(50)
>>> player.defend()
>>> player.exp

방어하기
3

 

하나만 더 하자 길다.

 

>>> class Calculator: # 부모클래스 역할
>>>     def __init__(self,num = 0):
>>>         self.num = num
        
# 한개의 정수를 받아 self.num 변수에 더하기
>>>     def add(self,num): 
>>>         pass

# 한개의 정수를 받아 self.num 변수에 빼기
>>>     def sub(self,num): 
>>>         pass

# 한개의 정수를 받아 self.num 변수에 곱하기
>>>     def mul(self,num): 
>>>         pass

# 한개의 정수를 받아 self.num 변수에 나누기
>>>     def div(self,num): 
>>>         pass

>>>     def result(self):
>>>         return self.num

>>>     def reset(self):
>>>         self.num = 0

 

pass 코드는 아무 의미 없음 함수 밑에 뭘 꼭 써줘야하는데 써줄게 없으니까 그냥 넘어가라고 붙인 코드

 

>>> class MyCalculator(Calculator):
>>>     def __init__(self,num):
>>>         super().__init__(num)

>>>     def add(self,num):
>>>         if self.check_num(num):
>>>             self.num += num

>>>     def sub(self,num):
>>>         if self.check_num(num):
>>>             self.num -= num

>>>     def mul(self,num):
>>>         if self.check_num(num):
>>>             self.num *= num

>>>     def div(self,num):
>>>         if self.check_num(num):
>>>             self.num /= num

>>>     def check_num(self,num):
>>>         is_num = True
>>>         if type(num) is not int:
>>>             print("입력값이 정수가 아닙니다.")
>>>             is_num = False
>>>         return is_num

 

>>> my_calculator = MyCalculator(0)

>>> my_calculator.add("")
입력값이 정수가 아닙니다.

my_calculator.add(9)

my_calculator.result()

9

 

private 화

  • 클래스의 인스턴스 변수나 메서드를 클래스 내부에서만 사용
  • 맹글링 (mangling) 기법을 이용해서 외부에서 직접적으로 변수나 메서드에 접근을 막을 수 있다

 

>>> class Wizard:
>>>     def __init__(self,hp,exp,mp):
>>>         self.hp = hp
>>>         self.exp = exp
>>>         self.__mp = mp

>>>     def __magic_skill(self):
>>>         print("마법 공격하기")
>>>         self.__mp -= 2

>>>     def attack(self):
>>>         print("공격하기")
>>>         self.exp += 3
>>>         self.__magic_skill()

 

>>> player = Wizard(100,0,50)

>>> player.attack()

공격하기
마법 공격하기

 

getter & setter

  • 인스턴스 변수에 접근할 때 특정 로직을 거쳐서 접근시킴
  • getter 또는 setter를 정의할 때는 private화 하기

 

>>> class Wizard:
>>>     def __init__(self,hp,exp,mp):
>>>         self.hp = hp
>>>         self.exp = exp
>>>         self.mp = mp

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

>>>     def attack(self):
>>>         print("공격하기")
>>>         self.exp += 3
    
>>>     @property
>>>     def mp(self):  #getter
>>>         print("getter 동작")
>>>         return self.__mp
    
>>>     @mp.setter
>>>     def mp(self,mp):
>>>         print("setter 동작")
>>>         if mp < 0:
>>>             mp = 0
>>>         self.__mp = mp

 

>>> player = Wizard(100,0,50)
setter 동작

>>> player.mp
getter 동작

50

>>> player.magic_skill()
마법 공격하기
getter동작
setter 동작

 

매직 메서드

  • 메서드명이 두개의 언더바로 감싼 메서드
  • 파이썬의 다양한 내장함수들이 객체의 매직 메서드를 실행해 결과를 반환해줌
  • __init__ 매직 메서드

 

>>> class MyDataset:
>>>     def __init__(self,data):
>>>         self.data = data

>>>     def __call__(self):
>>>         print("함수 실행 방법처럼 객체를 함수 호출 하듯이 만들어 주는 메서드")

>>>     def __str__(self): # print 함수에 이 객체를 넣으면 이 매직메서드에서 반환된 결과를 출력해준다.
>>>         return "MyDataset Class"
    
>>>     def __len__(self): #  len 함수에 이 클래스에 객체를 넣으면 이 매직 메서드를 호출한다고 생각하면 됩니다.
>>>         return len(self.data)

>>>     def __getitem__(self,idx): # 인덱싱과 슬라이싱을 가능하게 해주는 매직 메서드
>>>         return self.data[idx]

 

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

>>> dt()
함수 실행 방법처럼 객체를 함수 호출 하듯이 만들어 주는 메서드

>>> print(dt)
MyDataset Class

>>> len(dt)
50

>>> dt[-1]
99

>>> dt[:20}
[50,
 51,
 52,
 53,
 54,
 55,
 56,
 57,
 58,
 59,
 60,
 61,
 62,
 63,
 64,
 65,
 66,
 67,
 68,
 69]

 

클래스 데코레이터

 

>>> class RunTracking:
>>>     def __init__(self,org_func):
>>>         self.org_func = org_func

>>>     def __call__(self,*args,**kwargs):
>>>         print("함수의 실행 시간을 기록합니다.")
>>>         result = self.org_func(*args,**kwargs)
>>>         print("함수의 실행 종료시간을 기록합니다.")
>>>         return result

 

>>> @RunTracking  
>>> def preprocess_data(data):
>>>     print("데이터를 전처리하는 중입니다.")
>>>     result = []
>>>     for item in data:
>>>         result.append(item**2)
>>>     return result

 

>>> data = list(range(10000000))
>>> result = preprocess_data(data)
데이터를 전처리하는 중입니다

 

뭐라 씨부엉씨부엉 모르겠따

근데! 이제 대충 읽으면 언어가 읽히긴 읽힌다 구현은 못해도