데이터과학 삼학년

Instance method, Class method, Static method 본문

Python

Instance method, Class method, Static method

Dan-k 2020. 4. 4. 17:18
반응형

python class를 사용할 때, 가장 헷갈리는 것이

Instance method, Class method, Static method 의 사용 의도(?) 차이이다.

 

이번에는 위 3가지 method의 차이점을 알아보고자 한다.

 

Instance method

class에서 instance를 만들고, instance를 통해서만 호출이 되는 method를 Instance method라고 한다.

흔히 class 안에서 만들어준 함수를 의미한다.

# -*- coding: utf-8 -*-

class Employee(object):
    
    raise_amount = 1.1  #1 연봉 인상율 클래스 변수
    
    def __init__(self, first, last, pay):
        self.first = first
        self.last = last
        self.pay = pay
        self.email = first.lower() + '.' + last.lower() + '@schoolofweb.net'
    
    def apply_raise(self):
        self.pay = int(self.pay * self.raise_amount)
        
    def full_name(self):
        return '{} {}'.format(self.first, self.last)
        
    def get_pay(self):
        return '현재 "{}"의 연봉은 "{}"입니다.'.format(self.full_name(), self.pay)

emp_1 = Employee('Sanghee', 'Lee', 50000)
emp_2 = Employee('Minjung', 'Kim', 60000)

# 연봉 인상 전
print(emp_1.get_pay())
print(emp_2.get_pay())

# 연봉 인상
emp_1.apply_raise()
emp_2.apply_raise()

# 연봉 인상 후
print(emp_1.get_pay())
print(emp_2.get_pay())


=========================================
현재 "Sanghee Lee"의 연봉은 "50000"입니다.
현재 "Minjung Kim"의 연봉은 "60000"입니다.
현재 "Sanghee Lee"의 연봉은 "55000"입니다.
현재 "Minjung Kim"의 연봉은 "66000"입니다.

이 안에 있는 get_pay, apply_raise, get_pay 가 instance method 이다.

 

 

Class method

class method는 class를 통해 호출되는 함수이다. class명.함수명 으로 호출해서 사용하면 되는데 class method는 주로 class안에서 일어나는 변화(설정 값 등)을 주고 싶을 때 사용한다.

예를 들면 아래 코드에는 raise_amount가 1.1로 고정되어 있는 class이다.

여기서 raise_amount를 변화시켜서 적용할 경우, class 안에 있는 코드 자체를 수정할 수 있으나, 

그것보다는 class 안에 class method를 만들어 class method를 통해 class 안의 코드 설정을 변화시키는 방법을 택할 수 있다.

# -*- coding: utf-8 -*-
import sys

class Employee(object):
    
    raise_amount = 1.1  # 연봉 인상율 클래스 변수
    
    def __init__(self, first, last, pay):
        self.first = first
        self.last = last
        self.pay = pay
    
    def apply_raise(self):
        self.pay = int(self.pay * self.raise_amount)
        
    def full_name(self):
        return '{} {}'.format(self.first, self.last)
        
    def get_pay(self):
        return '현재 "{}"의 연봉은 "{}"입니다.'.format(self.full_name(), self.pay)
    
    #1 클래스 메소드 데코레이터를 사용하여 클래스 메소드 정의
    @classmethod
    def change_raise_amount(cls, amount):
        #2 인상율이 "1" 보다 작으면 재입력 요청
        while amount < 1:
            print('[경고] 인상율은 "1"보다 작을 수 없습니다.')
            amount = input('[입력] 인상율을 다시 입력하여 주십시오.\n=> ')
            amount = float(amount)
        cls.raise_amount = amount
        print('인상율 "{}"가 적용 되었습니다.'.format(amount))

emp_1 = Employee('Sanghee', 'Lee', 50000)
emp_2 = Employee('Minjung', 'Kim', 60000)

# 연봉 인상 전
print(emp_1.get_pay())
print(emp_2.get_pay())

# 연봉 인상율 변경
Employee.change_raise_amount(0.9)

# 연봉 인상
emp_1.apply_raise()
emp_2.apply_raise()

# 연봉 인상 후
print(emp_1.get_pay())
print(emp_2.get_pay())


=========================================
현재 "Sanghee Lee"의 연봉은 "50000"입니다.
현재 "Minjung Kim"의 연봉은 "60000"입니다.
[경고] 인상율은 "1"보다 작을 수 없습니다.
[입력] 인상율을 다시 입력하여 주십시오.
=> 1.2
인상율 "1.2"가 적용 되었습니다.
현재 "Sanghee Lee"의 연봉은 "60000"입니다.
현재 "Minjung Kim"의 연봉은 "72000"입니다.

그 이외에도 class method는 인스턴스 인자를 받을때 바꿔주는 역할도 할 수 있다.

예를 들어 주민등록 번호를 파싱하여 instance 에 인자를 넣어주는 코드를 만든다면, 아래 코드처럼 class 밖에서 함수를 만들어 적용하는 방법이 있다.

# -*- coding: utf-8 -*-
class Person(object):
    def __init__(self, year, month, day, sex):
        self.year = year
        self.month = month
        self.day = day
        self.sex = sex
        
    def __str__(self):
        return '{}년 {}월 {}일생 {}입니다.'.format(self.year, self.month, self.day, self.sex)
    
ssn_1 = '900829-1034356'
ssn_2 = '051224-4061569'

def ssn_parser(ssn):
        front, back = ssn.split('-')
        sex = back[0]
        
        if sex == '1' or sex == '2':
            year = '19' + front[:2]
        else:
            year = '20' + front[:2]
            
        if (int(sex) % 2) == 0:
            sex = '여성'
        else:
            sex = '남성'
            
        month = front[2:4]
        day = front[4:6]
        
        return year, month, day, sex

person_1 = Person(*ssn_parser(ssn_1))
print(person_1)

person_2 = Person(*ssn_parser(ssn_2))
print(person_2)


=========================================
1990년 08월 29일생 남성입니다.
2005년 12월 24일생 여성입니다.

그러나 위 방법보다는

class 안에 ssn_parser를 class method로 설정해 줌으로써 Person.ssn_parser(ssn) 으로 인자를 만들 수도 있다. 아래 코드 처럼

# -*- coding: utf-8 -*-
class Person(object):
    def __init__(self, year, month, day, sex):
        self.year = year
        self.month = month
        self.day = day
        self.sex = sex
        
    def __str__(self):
        return '{}년 {}월 {}일생 {}입니다.'.format(self.year, self.month, self.day, self.sex)
    
    @classmethod
    def ssn_constructor(cls, ssn):
        front, back = ssn.split('-')
        sex = back[0]
        
        if sex == '1' or sex == '2':
            year = '19' + front[:2]
        else:
            year = '20' + front[:2]
            
        if (int(sex) % 2) == 0:
            sex = '여성'
        else:
            sex = '남성'
            
        month = front[2:4]
        day = front[4:6]
        
        return cls(year, month, day, sex)
    
ssn_1 = '900829-1034356'
ssn_2 = '051224-4061569'

person_1 = Person.ssn_constructor(ssn_1)
print(person_1)

person_2 = Person.ssn_constructor(ssn_2)
print(person_2)


=========================================
1990년 08월 29일생 남성입니다.
2005년 12월 24일생 여성입니다.

위 코드는 class method를 씀으로써 주민등록번호를 파싱하여 바로 인스턴스를 만드는 코드이다. 

class method는 class를 만드는데 있어서 class의 내용변화에 대비해서 미리 만들어 놓으면 참 유용하게 쓸 수 있을 것이다.

classmethod에서 인자로 정의한 cls 는 꼭 cls로 쓰지 않아도 되지만, 암묵적으로 이렇게들 쓴다. cls는 해당 class 명이라고 봐도 된다. cls==Person 

아래 코드는 tensorflow에서 주로 쓰는 방법이다.

%%writefile custom_prediction.py

import os
import pickle
import numpy as np

from datetime import date
from google.cloud import logging

import tensorflow.keras as keras


class CustomModelPrediction(object):   
    def __init__(self, model, processor): 
        self._model = model
        self._processor = processor       
    
    def _postprocess(self, predictions):
        labels = ['negative', 'positive']
        return [
            {
                "label":labels[int(np.round(prediction))],
                "score":float(np.round(prediction, 4))
            } for prediction in predictions]
    
    def predict(self, instances, **kwargs):       
        preprocessed_data = self._processor.transform(instances)
        predictions =  self._model.predict(preprocessed_data)
        labels = self._postprocess(predictions)
        return labels

    @classmethod
    def from_path(cls, model_dir):       
        model = keras.models.load_model(
          os.path.join(model_dir,'keras_saved_model.h5'))
        with open(os.path.join(model_dir, 'processor_state.pkl'), 'rb') as f:
            processor = pickle.load(f)           
        return cls(model, processor)

 

Static method

static method는 얼핏 보면 class method와 큰 차이가 없어보이지만.

사실 class 밖에 있는 함수랑 큰 차이가 없다.

다만 해당 method를 class 의 특성을 갖는 method로 분류하기 위에 넣어 놓은 것이다.

특히 static method는 함수를 정의할 때, 함수내에서 인자를 따로 받지 않는다. (classmethod는 class를 받고, instancemethod는 self를 인자로 넣어줌)

 

많은 사람들이 클래스 메소드와 스태틱 메소드를 혼동하는데, 이 강좌에서 인스턴스 메소드, 클래스 메소드, 스태틱 메소드 이 세 가지 메소드의 개념을 확실히 잡고 가도록 하죠.
이 세 가지 메소드는 모두 클래스 안에서 정의 됩니다.
인스턴스 메소드는 인스턴스를 통해서 호출이 되고, 첫 번째 인자로 인스턴스 자신을 자동으로 전달합니다. 관습적으로 이 인수를 'self'라고 칭합니다.
클래스 메소드는 클래스를 통해서 호출이 되고 "@classmethod"라는 데코레이터로 정의합니다. 첫 번째 인자로는 클래스 자신이 자동으로 전달되고 이 인수를 관습적으로 'cls'라고 칭합니다.
스태틱 메소드는 앞서 설명한 두 메소드와는 틀리게 인스턴스나 클래스를 첫 번째 인자로 받지 않습니다. 스태틱 메소드는 클래스 안에서 정의되어 클래스 네임스페이스 안에는 있을뿐 일반 함수와 전혀 다를게 없습니다. 하지만 클래스와 연관성이 있는 함수를 클래스 안에 정의하여 클래스나 인스턴스를 통해서 호출하여 조금 편하게 쓸 수가 있는 것 입니다.

# -*- coding: utf-8 -*-
class Person(object):
    
    my_class_var = 'sanghee'
    
    def __init__(self, year, month, day, sex):
        self.year = year
        self.month = month
        self.day = day
        self.sex = sex
        
    def __str__(self):
        return '{}년 {}월 {}일생 {}입니다.'.format(self.year, self.month, self.day, self.sex)
    
    @classmethod
    def ssn_constructor(cls, ssn):
        front, back = ssn.split('-')
        sex = back[0]
        
        if sex == '1' or sex == '2':
            year = '19' + front[:2]
        else:
            year = '20' + front[:2]
            
        if (int(sex) % 2) == 0:
            sex = '여성'
        else:
            sex = '남성'
            
        month = front[2:4]
        day = front[4:6]
        
        return cls(year, month, day, sex)
    
    @staticmethod
    def is_work_day(day):
        # weekday() 함수의 리턴값은
        # 월: 0, 화: 1, 수: 2, 목: 3, 금: 4, 토: 5, 일: 6
        if day.weekday() == 5 or day.weekday() == 6:
            return False
        return True
    
ssn_1 = '900829-1034356'
ssn_2 = '051224-4061569'

person_1 = Person.ssn_constructor(ssn_1)
print(person_1)

person_2 = Person.ssn_constructor(ssn_2)
print(person_2)

import datetime
# 일요일 날짜 오브젝트 생성
my_date = datetime.date(2016, 10, 9)

# 클래스를 통하여 스태틱 메소드 호출
print(Person.is_work_day(my_date))
# 인스턴스를 통하여 스태틱 메소드 호출
print(person_1.is_work_day(my_date))


=========================================
1990년 08월 29일생 남성입니다.
2005년 12월 24일생 여성입니다.
False
False

 

 

출처 : http://schoolofweb.net/blog/posts/%ED%8C%8C%EC%9D%B4%EC%8D%AC-oop-part-4-%ED%81%B4%EB%9E%98%EC%8A%A4-%EB%A9%94%EC%86%8C%EB%93%9C%EC%99%80-%EC%8A%A4%ED%83%9C%ED%8B%B1-%EB%A9%94%EC%86%8C%EB%93%9C-class-method-and-static-method/

 

SchoolofWeb :: 파이썬 - OOP Part 4. 클래스 메소드와 스태틱 메소드 (Class Method and Static Method)

파이썬 객체 지향 프로그래밍(Object Oriented Programming) 강좌 - Part 4. 클래스 메소드와 스태틱 메소드 (Class Method and Static Method)

schoolofweb.net

 

728x90
반응형
LIST
Comments