🐍Python | Django

[Python] Decorator

이줭 2022. 4. 18. 22:47
728x90

Decorator란 이미 작성된 함수에 새로운 기능을 추가하여 함수를 확장시키는 개념으로, 일반적으로 함수의 전처리나 후처리에 대한 필요가 있을 경우 사용한다. 함수명 위에 @데코레이터로 사용할 함수 이름을 적어주면 된다. 예시를 보자.

 

만약, 서버에서 사용자에 관련된 메서드를 만든다고 하면 다음과 같은 순서로 처리하게 될 것이다.

# 1. 권한체크 (전처리)
# 2. 요청에 대한 처리 (본 로직)
# 3. 로그 수집 또는 오류 처리 (후처리)

def create_user(*args):
    if check_auth():
        # main logic
    log()
    
def update_user(*args):
    if check_auth():
    	# main logic
    log()
    
def delete_user(*args):
    if check_auth():
    	# main logic
    log()

예시 코드를 보면 같은 작업이 전, 후로 반복되는 것을 볼 수 있다.

 

여기에 Docorator를 적용해보면 다음과 같다.

def deco(func):
    def wrapper(*args):
    	if auth_check():
        	func(*args)
        log()
    return wrapper
    
@deco
def create_user(*args):
    # main logic
    
@deco
def update_user(*args):
    # main logic
    
@deco
def delete_user(*args):
    # main logic

Decorator를 사용한다고 성능상 나아지는 것은 없다.

 

시스템이 커질수록 관리하기 편하고, 소스코드 또한 주요 로직만 보니 가독성이 좋아지는 장점이 있다. @method_name을 사용하지 않아도, 다음과 같이 사용할 수도 있다.

def deco(func):
    def wrapper(*args):
        if auth_check():
            func(*args)
        log()
    return wrapper
    
def create_user(*args):
	# main logic
    
deco_create_user = deco(create_user(*args))

만약 Decorator를 두개 사용한다면 어떻게 될까?

def star(func):
    def inner(*args, **kwargs):
        print("*" * 30)
        func(*args, **kwargs)
        print("*" * 30)
    return inner


def percent(func):
    def inner(*args, **kwargs):
        print("%" * 30)
        func(*args, **kwargs)
        print("%" * 30)
    return inner


@star
@percent
def printer(msg):
    print(msg)


printer("Hello")

결과는 다음과 같다. @star는 @percent를 데코레이트 하고, @percent는 printer를 데코레이트 한다.

******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Hello
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************

 

피보나치 수열을 구하는 함수를 만들어 보면 다음과 같이 메모이제이션을 통해 반복 계산을 줄일 수 있다.

res = {}
def fibo(n):
    if n < 3:
        return 1
    if res.get(n):
        return res.get(n) 

    return fibo(n - 1) + fibo(n - 2)

위의 코드에서 Decorator를 적용해보면, res 변수를 Decorator 안으로 넣을 수 있고, 가독성을 증가시킬 수 있다.

def fibo_deco(func):
    res = {}
    
    def inner(*args):
        n = args[0]
        if res.get(n):
            return res.get(n)
        r = func(n)
        res[n] = r
        return r
        
    return inner
    
@fibo_deco
def fibo(n):
    if n < 3:
        return 1
        
    return fibo(n - 1) + fibo(n - 2)

Decorator를 사용할 때 주의해야 할 점으로는 데코레이터를 사용한 함수는 속성이 사라진다는 것이다.

def deco(func):
    def inner(*args):
        func(*args)
        
    return inner
         
 @deco
 def test():
     '''
     테스트 함수
     '''
     pass
         
 print(test.__name__) # inner
 print(test.__doc__) # None

 

test함수의 __name__과 __doc__을 출력해보면 __name__은 inner, __doc__는 None으로 출력되는 것을 확인할 수 있다. 이러한 부분을 방지하기 위해서 wraps를 사용할 수 있다.

from functools import wraps

def deco(func):
    @wraps(func)
    def inner(*args):
        func(*args)
        
    return inner
         
 @deco
 def test():
     '''
     테스트 함수
     '''
     pass
         
 print(test.__name__) # test
 print(test.__doc__) # 테스트 함수

wraps를 사용하여 기존 함수의 속성을 유지시킬 수 있다는 것을 확인할 수 있다.

728x90

'🐍Python | Django' 카테고리의 다른 글

[Python] main 함수  (0) 2022.04.26
[Python] Class와 상속(Inheritance)  (0) 2022.04.19
[Python] Under Score(_)?  (0) 2022.04.14
[Python] String join()/split()  (0) 2022.02.07
[Python] strftime과 strptime  (0) 2021.08.25