본문 바로가기

Programming/Python

[Python] instance method / class method / static method

https://www.daleseo.com/python-class-methods-vs-static-methods/  메서드에서 헷갈리는 부분을 정리하기 위해 이 분의 글을 참고하였다! 

 

[파이썬] 정적(static) 메서드와 클래스(class) 메서드

Engineering Blog by Dale Seo

www.daleseo.com

 

인스턴스 메서드(instance method)

 

일반적으로, 클래스 내에서 데코레이터(decorator)없이 메서드를 선언하게 되는 경우.

첫 번째 매개변수로 클래스의 인스턴스, self가 넘어오고, self를 통해 인스턴스 메서드가 인스턴스 속성(attribute)에 접근하거나 다른 인스턴스 메서드를 호출할 수 있다.

또, self를 통해 클래스 속성에 접근하거나 클래스 메서드를 호출할 수도 있다.

 

이때, 인스턴스를 통해 인스턴스 변수에 접근하는 것은 좋지 않으며, 메서드를 만들어서 접근하는 것이 좋다.

또한,  클래스 자체를 통해 클래스 변수에 접근해 수정하는 것도 좋지 않아 클래스 메서드를 만들어서 사용하는 것이 좋다. 

 

class Cal:
    def __init__(self, value = 0):
        self._value = value

    def increase(self, x):
        self._value += x

    def decrease(self, x):
        self._value -= x

cnt = Cal()
print("초기 생성자 : ", cnt._value)

cnt.increase(5)
print("increase 후 : ", cnt._value)

cnt.decrease(2)
print("decrease 후 : ", cnt._value)
초기 생성자 :  0
increase 후 :  5
decrease 후 :  3

 

Cal 클래스는 3개의 인스턴스 메서드로 이루어져 있고, 각 인스턴스 메서드가 첫 번째 매개변수로 self를 가지고 있다.

생성자인 __init__ 메서드에서 인자로 넘어오게 되는 vlaue 값을 객체의 _value 속성에 할당하고, increase()와 decrease() 메서드를 통해 객체의 _value 속성값을 변경할 수 있다. 

 

이때, 인스턴스 메서드는 cnt = Cal() 와 같이 인스턴스를 먼저 생성한 수에 호출해야 한다.

인스턴스 메서드의 첫 번째 매개변수인 self는 자동으로 할당되어, 호출 시 넘기는 첫 번째 인자 = 인스턴스 메서드에서 두 번째 매개변수 에 할당된다. 

 

클래스 메서드(class method)

 

@classmethod 라는 데코레이터를 사용해 클래스 메서드 선언.

 

첫 번째 매개변수로 클래스 자체가 넘어온다. (클래스 인스턴스가 아님!)

클래스 자체가 넘어오게 되는 첫 번째 매개변수 이름은 보통 ' cls ' 라고 하며,  클래스 메서드는 cls를 통해 클래스 속성에 접근하거나, 클래스 메서드를 호출할 수 있다.

 

이때, cls를 통해서는 인스턴스 속성에 접근하거나, 다른 인스턴스 메서드를 호출하는 것이 불가능하다. 

 

class Car():
    def __init__(self, company, price):
        self._company = company
        self._price = price

    @classmethod
    def fromTuple(cls, tuple):
        return cls(tuple[0], tuple[1])

    @classmethod
    def fromDictionary(cls, dic):
        return cls(dic['company'], dic['price'])

car = Car('BMW', 3000)
print('기본 생성자로 객체 생성 : ', car._company, car._price)

car = Car.fromTuple(("BMW", 3000))
print('클래스 메서드 - 튜플로부터 객체 생성 : ', car._company, car._price)

car = Car.fromDictionary({'company' : "BMW", 'price' : 3000})
print('클래스 메서드 - 딕셔너리로부터 객체 생성 : ', car._company, car._price)
기본 생성자로 객체 생성 :  BMW 3000
클래스 메서드 - 튜플로부터 객체 생성 :  BMW 3000
클래스 메서드 - 딕셔너리로부터 객체 생성 :  BMW 3000

위에서, Car 클래스는 생성자 1개인 인스턴스 메서드 & @classmethod 어노테이션이 달린 2개의 클래스 메서드로 이루어져 있다.

fromTuple()과 fromDictionary() 메서드의 첫 번째 매개변수인 cls에는 클래스가 할당되어 있어, 

이 클래스를 호출하면 -> 생성자가 호출되고 -> 인스턴스가 생성된다. 즉, Car 클래스의 객체 car를 생성해준다. 

 

정적 메서드(static method)

 

@staticemethod 데코레이터를 사용해 정적 메서드를 선언.

정적 메서드는 인스턴스 메서드나 클래스 메서드와 달리 첫 번째 매개 변수가 할당되지 않는다. 

따라서, 정적 메서드 내에서는 인스턴스/클래스 속성에 접근하거나, 인스턴스/클래스 메서드를 호출할 수 없다.

 

class StringUtils():

    @staticmethod
    def toCamelcase(text):
        words = iter(text.split('_'))  
        # words = ['last', 'modified', 'date']
        # <list_iterator object at 0x1053f1c10>
        # next(words) 한 번 : last
        # 문자열 내 띄어쓰기 기준으로 각 단어의 첫글자는 대문자로, 나머지는 소문자로 변환.
        return next(words) + "".join(i.title() for i in words)
        
    @ staticmethod
    def toSnakeCase(text):
        letters = ['_' + i.lower() if i.isupper() else i for i in text]
        # ['l', 'a', 's', 't', '_m', 'o', 'd', 'i', 'f', 'i', 'e', 'd', '_d', 'a', 't', 'e']
        return "".join(letters)
        # return "".join(letters).lstrip("_")  # 위의 letters에서 첫 단어의 첫 글자가 대문자일 때 _소문자로 변경되는 부분 처리


print(StringUtils.toCamelcase("last_modified_date"))
print(StringUtils.toSnakeCase("lastModifiedDate"))
toCamelCase() 함수 실행 :  lastModifiedDate
toSnakeCase() 함수 실행 :  last_modified_date

 

toCamelCase()와 toSnakeCase() 메서드는 매개변수로 넘어온 문자열에만 의존하는 함수로, 굳이 클래스의 일부로 선언할 필요가 없지만 비슷한 종류의 유틸리티 메서드를 하나의 클래스로 묶어둘 때 정적 메서드로 선언할 수 있다. 

 

여기서, Iterable Object(__iter__: tile, list, generator 등) : 이터레이션을 수행할 공간의 객체

Iterator Object(__next__) : 수행 처리 객체로서, 

iter, next는 iter를 이용해 이터레이터(iterator, 반복을 이용해 어떤 처리를 수행하는 객체)를 만들고 next로 반복을 수행하도록 요청한다. 

더 이상 가져올 것이 없으면 StopIteration이 발생해 이 오류를 제거하면 자동화도 가능하다.

 

클래스 메서드와 정적 메서드의 공통점과 차이점

 

공통점: 별도의 인스턴스 생성없이 클래스를 대상으로 클래스 이름 뒤에 바로 . (오퍼레이터)를 붙여 호출할 수 있다.

차이점: 클래스 메서드를 호출할 때, 첫번째 인자로 클래스 자체가 넘어옴 -> 클래스 속성 접근 및 다른 클래스 함수 호출 가능

            정적 메서드를 호출할 때, 첫번째 인자로 아무것도 넘어오지 않음 -> 다른 인자만 접근 가능(클래스에 접근할 필요가 없을 때)