기록하는삶

[파이썬/python] 클래스(Class), 클래스 상속 본문

AI/파이썬(Python)

[파이썬/python] 클래스(Class), 클래스 상속

mingchin 2021. 9. 2. 17:36
728x90
반응형

파이썬은 객체지향(OOP _ Object Oriented Programming) 언어이다.

객체 = 속성(상태, 특징)과 행위(행동, 동작, 기능)로 구성된 대상

> 속성: '변수'로 구현

> 행위: '함수'로 구현

즉, 객체란 특정 목적을 달성하기 위한 변수와 함수들의 묶음이다.

ex) 아래 링크의 유튜브 강의에서는 class를 자기소개서에 빗대어 표현하였다.

https://www.youtube.com/watch?v=uIcK7-YU8uA 

파이썬에서 객체를 생성하는 방법인 Class에 대해 예시를 통해 살펴보자. 아래는 자전거의 정보(이름, wheel_size, color, model_code)를 통해 자전거를 등록하고, 등록된 자전거의 행위(move, turn, stop, auto_cruise) 등을 정의한 Class이다.

Class를 정의할 때 보통 snake_case가 아닌 CamelCase로 그 이름을 부여한다.
- snake_case: 띄어쓰기 대신 _를 사용하는 표기법. 변수, 함수명에 사용

- CamelCase: 띄어쓰기 대신 대문자를 사용하는 표기법. Class명에 사용

class Bicycle():
    num = 0 # 클래스변수 정의, 클래스 내 모든 인스턴스에 적용되는 공통된 변수
    def __init__(self, wheel_size, color, model_code):
        self.wheel_size = wheel_size
        self.color = color
        self.model_code = model_code
        Bicycle.num = Bicycle.num + 1
    def __str__(self):
        return "자전거의 wheel_size는 %d, 색은 %s, 코드는 %d입니다." % \
                (self.wheel_size, self.color, self.model_code)
    def __add__(self, other):
        return "Class 객체 간의 더하기를 정의합니다. 두 자전거의 코드 합은 %d입니다." % \
                (self.model_code + other.model_code)
    def move(self, speed):
        print("{1}자전거: 시속 {0:03d}킬로미터 전진".format(speed, self.color), end='\t')
        self.speed = speed
    def turn(self, direction):
        print("{0}자전거: {1}회전".format(self.color, direction))
    def stop(self):
        print("자전거({0},{1}): 정지".format(self.wheel_size, self.color))
    def auto_cruise(self):
        self.move(self.speed)
        print("자율주행시작")
    
    @staticmethod
    def check_type(model_code):
        if model_code == 10:
            print("이 자전거는 일반자전거입니다.")
        elif model_code == 20:
            print("이 자전거는 전기자전거입니다.")
        elif model_code == 30:
            print("이 자전거는 하이브리드자전거입니다.")
    @classmethod
    def count_bi(cls):
        print("등록된 총 자전거 수는 {0}대 입니다.".format(cls.num))

1) __init__(self, 변수1, 변수2, 변수3, ...)

Class가 선언과 동시에 실행되는, Class 선언을 위해 필수적으로 필요한 함수이다. self는 선언할 때의 변수명을 의미하고 변수 1,2,3은 선언할 때 입력해주어야 하는 변수들, 즉 일종의 형식을 말한다. 위의 예시에서 Bicycle이라 선언되기 위해서는 변수명(self)와 3개의 변수가 필요하다.

my_bicycle = Bicycle(30, 'white', 10)
your_bicycle = Bicycle(33, 'blue', 20)
super_bicycle = Bicycle(35, 'red', 30)

다음과 같이 정의하면 총 3개의 자전거가 각 my_bicycle, your_bicycle, super_bicycle이란 이름으로 등록된다. 이때 각각을 인스턴스(instance)라 부른다.

 

2) __str__(self)

양 쪽에 두 개의 언더바(__)로 둘러쌓인 함수는 예약된 함수다. 그 중 하나인 __str__는 클래스 선언 후 해당 클래스를 print 했을 때의 결과를 저장해놓는 함수다.

따로 __str__() 함수를 지정하지 않는다면, 아래의 결과를 반환한다.

3) __add__(self, other)

마찬가지로 예약 함수 중 하나로, 선언된 두 객체의 합을 정의할 수 있다.

4) 클래스 변수(class variable)와 인스턴스 변수(instance variable)

instance가 선언되기 이전에 정의된 변수 'num = 0'의 경우 모든 하위 객체가 공통으로 가지는 클래스 변수(class variable)이다. 반면 __init__ 함수 아래에 정의된 self.xxx의 변수들의 경우 instance가 선언됨과 동시에 정의되므로 인스턴스 변수(instance variable)이고, 해당 instance에서만 사용된다. 정의된 변수들은 self.xxx로 접근이 가능하다.

5) __init__ 제외 하위 함수들(move, turn, stop, auto_cruise)

모두 인스턴스명(self).함수명(변수)의 형태로 활용될 수 있다. 사용 예시는 아래와 같다.

6) @staticmethod _ 정적 메서드

클래스와 관련있어 내부에 정의하지만, 클래스에 정의되는 인스턴스들과 무관하게 독립적으로 동작하는 함수들은 @staticmethod를 선언하고 그 아래에 정의한다. 예시에서 자전거 종류를 code에 따라 구분하는 행위(함수)는 정의되는 하위 인스턴스들과는 무관하다.

 

7) @classmethod _ 클래스 메서드

위의 변수의 구분에서 'num = 0'은 하위 인스턴스들과 무관한 클래스 변수였다. 이러한 클래스 변수를 활용하는 함수들은 @classmethod를 선언하고 그 아래에 정의한다.

또 다른 예시를 살펴보자.

class Robot():
    def __init__(self, name, pos):
        self.name = name
        self.pos = pos
        
    def move(self, step):
        self.pos = self.pos + step
        print("{0} position : {1}".format(self.name, self.pos))

robot1 = Robot('R1',0)
robot2 = Robot('R2', 10)

robot1.move(10)

로봇 클래스를 정의하고, 이동을 하는 간단한 코드다. 이렇게 이미 선언되어 있는 클래스가 가진 변수 혹은 행위를 그대로 이용하되, 추가하고 싶은 경우에 클래스 상속을 이용할 수 있다.

 

class AIrobot(Robot):
    def __init__(self, name, pos, state, bootnum):
        #Robot.__init__(self, name, pos)와 같은 기능, 부모에게서만 정의한 것을 사용하려면 필요
        super().__init__(name, pos) 
        self.state = state
        self.bootnum = bootnum
    def reboot(self):
        self.state = "rebooting"
        print("{0}, {1} 위치에서 재부팅을 시작합니다.".format(self.name, self.pos))
        self.bootnum = self.bootnum + 1
    def booted(self):
        self.state = "on"
        print("{0}, {1} 위치에서 부팅을 완료했습니다.".format(self.name, self.pos))
    def intro(self):
        print("{0}는 AI로봇이며, 현재 {1}에 위치해있습니다. 지금까지 {2}번의 재부팅을 했습니다.".format(self.name, self.pos, self.bootnum))

robot100 = AIrobot("R100", 12, 'on', 0)
robot101 = AIrobot("현아", 20, 'on', 0)

두 가지만 기억하면 된다.

1) Class 선언시, '클래스이름(상속받고자 하는 클래스):' 

2) '__init__'의 아래에 'super().__init__(name, pos)' 을 통해 상속받고자 하는 클래스가 가졌던 __init__의 규칙 불러오기

 

나머지는 동일하다. 아래는 사용 예시.

[참고]

class SupervisedTrainer(object):
    """
    The SupervisedTrainer class helps in setting up training framework in a supervised setting.

    Args:
        optimizer (kospeech.optim.__init__.Optimizer): optimizer for training
        criterion (torch.nn.Module): loss function
        trainset_list (list): list of training datset
        validset (kospeech.data.data_loader.SpectrogramDataset): validation dataset
        num_workers (int): number of using cpu cores
        device (torch.device): device - 'cuda' or 'cpu'
        print_every (int): number of timesteps to print result after
        save_result_every (int): number of timesteps to save result after
        checkpoint_every (int): number of timesteps to checkpoint after
    """
    train_dict = {'loss': [], 'cer': []}
    valid_dict = {'loss': [], 'cer': []}
    train_step_result = {'loss': [], 'cer': []}
    TRAIN_RESULT_PATH = "train_result.csv"
    VALID_RESULT_PATH = "eval_result.csv"
    TRAIN_STEP_RESULT_PATH = "train_step_result.csv"

    def __init__(
            self,
            optimizer: Optimizer,                          # optimizer for training
            criterion: nn.Module,                          # loss function
            trainset_list: list,                           # list of training dataset
            validset: SpectrogramDataset,                  # validation dataset
            num_workers: int,                              # number of threads
            device: torch.device,                          # device - cuda or cpu
            print_every: int,                              # number of timesteps to save result after
            save_result_every: int,                        # nimber of timesteps to save result after
            checkpoint_every: int,                         # number of timesteps to checkpoint after
            teacher_forcing_step: float = 0.2,             # step of teacher forcing ratio decrease per epoch.
            min_teacher_forcing_ratio: float = 0.8,        # minimum value of teacher forcing ratio
            architecture: str = 'las',                     # model to train - las, transformer
            vocab: Vocabulary = None,                      # vocabulary object
            joint_ctc_attention: bool = False,             # flag indication whether joint CTC-Attention or not
    ) -> None:
        self.num_workers = num_workers
        self.optimizer = optimizer
        self.criterion = criterion
        self.trainset_list = trainset_list
        self.validset = validset
        self.print_every = print_every
        self.save_result_every = save_result_every
        self.checkpoint_every = checkpoint_every
        self.device = device
        self.teacher_forcing_step = teacher_forcing_step
        self.min_teacher_forcing_ratio = min_teacher_forcing_ratio
        self.metric = CharacterErrorRate(vocab)
        self.architecture = architecture.lower()
        self.vocab = vocab
        self.joint_ctc_attention = joint_ctc_attention

        if self.joint_ctc_attention:
            self.log_format = "step: {:4d}/{:4d}, loss: {:.6f}, ctc_loss: {:.6f}, ce_loss: {:.6f}, " \
                              "cer: {:.2f}, elapsed: {:.2f}s {:.2f}m {:.2f}h, lr: {:.6f}"
        # 현재 학습 중인게 아래의 결과를 찍는다.
        else:
            self.log_format = "step: {:4d}/{:4d}, loss: {:.6f}, " \
                              "cer: {:.2f}, elapsed: {:.2f}s {:.2f}m {:.2f}h, lr: {:.6f}"

        if self.architecture in ('rnnt', 'conformer'):
            self.rnnt_log_format = "step: {:4d}/{:4d}, loss: {:.6f}, " \
                                   "elapsed: {:.2f}s {:.2f}m {:.2f}h, lr: {:.6f}"

위의 예시처럼 __init__()을 정의할 때 파라미터들의 type과 default를 지정할 수 있다. 

또한 __init__() 뒤의 -> None이라는 표현은, __init__() 함수가 아무 것도 반환하지 않는 함수임을 표현하는 주석이다.

외에 __init__()도 함수의 일종이므로, if문을 통해 조건에 따라 다른 실행을 하도록 설정할 수 있다.

728x90
반응형