본문 바로가기

컴린이 일기장/Today I Learned

[TIL] Python 클래스의 메서드(method) + 데코레이터(decorater)

반응형

[주절주절]

코드 짜기 싫어서.. 괜히 딴 거 공부..🎭

 

[Today I Learned]

# 클래스 속성과 인스턴스 속성

클래스의 속성에는 클래스 속성과 인스턴스 속성 두 가지 종류가 있다.

1) 클래스 속성

class Person:
	bag = []
    
    def put_bag(self, stuff):
    	self.bag.append(stuff)

위와 같이 코드를 작성했을 경우, bag가 클래스 속성이 된다.

class Person:
    bag = []
    
    def put_bag(self, stuff):
    	self.bag.append(stuff)


Amy = Person()
Amy.put_bag('책')

Hye = Person()
Hye.put_bag('노트북')

print(Amy.bag, Hye.bag)

Amy와 Hye 인스턴스를 각각 만들고 put_bag 메서드로 물건을 넣었음에도 Amy.bag와 Hye.bag의 값이 동일한 것을 확인할 수 있다. 즉, 클래스 속성은 모든 인스턴스가 공유하게 된다. (해당 클래스 인스턴스가 몇 번 생성되었는지 등을 체크할 때 사용할 수 있겠다!) 이런 이유로 put_bag 메서드에서 클래스 속성 bag에 접근하기 위해 self를 사용하는 것은 조금 어색하다고 볼 수 있다. self는 현재 인스턴스를 뜻하기 때문이다. 따라서 다음과 같이 클래스 이름으로 클래스 속성에 접근하는 것이 좀 더 명확한 코드라고 볼 수 있다.

class Person:
	bag = []
    
    def put_bag(self, stuff):
    	Person.bag.append(stuff)
        
Amy = Person()
Amy.put_bag('책')

Hye = Person()
Hye.put_bag('노트북')

print(Amy.bag, Hye.bag)

클래스 바깥에서도 다음과 같이 클래스 이름으로 클래스 속성에 접근하면 된다.

print(Person.bag)

 

2) 인스턴스 속성

그렇다면 이 가방을 여러 사람이 공유하지 않게 하려면 어떻게 해야 할까? bag를 인스턴스 속성으로 만들면 된다.

class Person:
	def __init__(self):
    	self.bag = []
        
    def put_bag(self, stuff):
    	self.bag.append(stuff)
        
Amy = Person()
Amy.put_bag('책')

Hye = Person()
Hye.put_bag('노트북')

print(Amy.bag, Hye.bag)

 

 

# 인스턴스 메소드(instance method)

정적 메소드와 달리 인스턴스를 생성한 후에 호출이 가능하다. 클래스에서 아무 데코레이터 없이 메서드를 선언하면 인스턴스 메서드로 취급이 되며, 첫 번째 매개 변수로 클래스의 인스턴스가 넘어오게 된다. 이 매개 변수의 이름은 관행적으로 'self'라고 한다.

class Counter:
    def __init__(self, value):
        self.value = value

    def increment(self, delta=1):
        self.value += delta

    def decrement(self, delta=1):
        self.value -= delta

 

# 정적 메서드 (class method, static method)

정적 메소드를 지원하는 방법에는 두 가지가 있다.

1) 클래스 메서드 (class method)

@classmethod 데코레이터를 사용해서 클래스에 메서드를 선언할 수 있다. 클래스 메서드는 첫 번째 매개 변수로 클래스 인스턴스가 아닌 클래스 자체를 넘기며, 이 변수의 이름은 관행적으로 'cls'라고 한다. 

class User:
    def __init__(self, email, pw):
        self.email = email
        self.pw = pw

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

 

아래는 우리가 일반적으로 사용하는, 기본 생성자로 객체를 생성하는 방법이다.

user1 = User("a@test.com","1234")

print(user1.email,user1.pw)

그리고 아래는 클래스 메서드를 이용해 객체를 생성한 것이다.

user2 = User.fromTuple(("b@test.com","5678"))
print(user2.email,user2.pw)

이 메서드(fromTuple)을 호출하면 생성자가 호출되고 인스턴스가 생성된다.

클래스 메서드는 이 'cls'를 통해 클래스 속성에 접근하거나 클래스 메서드를 호출할 수 있다.

class User:
	
    num = 5

    def __init__(self, email, pw):
        self.email = email
        self.pw = pw

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

    @classmethod
    def print_num(cls):
        print(f'번호: {cls.num}')
user3 = User("c@test.com","1234")
User.print_num()

 

2) 정적 메서드 (static method)

@staticmethod 데코레이터를 사용해서 클래스에 메서드를 선언할 수 있다. 인스턴스 메서드나 클래스 메서드와 달리 첫 번째 매개 변수가 할당되지 않는다. 따라서 정적 메서드 내에서는 인스턴스/클래스 속성에 접근하거나 인스턴스/클래스 메서드를 호출하는 것이 불가능하다. 

class calc:

    @staticmethod
    def add(a,b):
        return a+b
        
calc.add(2,8)

이러한 특징 때문에 정적 메서드는 인스턴스의 상태를 변화시키지 않는 메서드를 만들 때 사용한다. 

 

정리하자면..

클래스 메서드와 정적 메서드는 인스턴스 생성없이 클래스를 대상으로 클래스 이름 뒤에 '.' 오퍼레이터를 붙여서 호출할 수 있다는 점이 동일하다. 차이점은 클래스 메서드는 클래스 속성에 접근하거나 다른 클래스 함수 호출이 가능하지만, 정적 메서드는 이것들이 불가능하다.

 

# 데코레이터 (decorator)

데코레이터는 '@'로 시작하는 것들로, 함수(메서드)를 장식한다는 의미에서 이런 이름이 붙었다. 장식자라고 부르기도 한다. 데코레이터는 일반적으로 함수를 수정하지 않고 기능에 변화를 주고 싶을 때(추가 기능을 구현하고 싶을 때) 사용한다.  

함수의 시작과 끝을 알리는 print 문을 추가하고 싶다는 상황을 가정해보자. 구현된 모든 함수의 시작과 끝에 print 문을 넣는 것은 매우 번거로운 일이다. 이런 경우에 데코레이터를 활용할 수 있다.

def trace(func):
    def wrapper():
        print(func.__name__, '함수 시작')
        func()
        print(func.__name__,'함수 끝')
    return wrapper

def hello():
    print('안녕')
    
trace_hello = trace(hello)
trace_hello()

먼저 데코레이터 trace는 호출할 함수를 매개변수로 받는다. 그리고 trace 함수 안에서 호출할 함수를 감싸는 함수 wrapper를 만든다. wrapper 함수에서는 함수 시작을 알리는 문자열을 출력하고, func을 호출한다. 그리고 함수 끝을 알리는 문자열을 출력한다. 끝으로 wrapper 함수 자체를 return을 통해 반환한다.

'@'를 사용하면 좀 더 간편하게 데코레이터를 사용할 수 있다.

@trace
def hello():
    print('안녕')

hello()

 

 

[질문 노트]

Q. 그런데 파이썬은 다른 언어처럼 엄격하게 정적 메서드를 적용하지 않는데, 그럼 실제로 개발 단계에선 어떤 목적으로 정적 메서드를 사용할까?

+ 흠... 메서드, 데코레이터.. 알겠는데 모르겠다. ( =모르겠다) 백엔드는 모르겠는데 딥러닝에선 많이 사용할지 모르겠다. 🙄

 

참고

dojang.io/mod/page/view.php?id=2378

www.daleseo.com/python-class-methods-vs-static-methods/

mygumi.tistory.com/253 

medium.com/@hckcksrl/python-%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0-decorator-980fe8ca5276

dojang.io/mod/page/view.php?id=2427

반응형