[ Python ] Decorator - 슬기로운 파이썬 트릭 책 中

2019. 9. 29. 18:49분석 Python/책 구현 리뷰

728x90

간략하게 유용한 경우부터

  • 로그 남기기
  • 접근 제어와 인증 시행
  • 계측 및 시각 측정
  • 비율 제한
  • 캐싱 및 기타

일단 파이썬 같은 경우에는 일급 함수

함수는 객체다 / 함수는 다른 함수 내부에서 정의될 수 있다.

Decorator란?

다른 함수를 `장식`하거나  `포장`하고 감싼 함수가 실행되기 전과 후에 다른 코드를 실행할 수 있게 한다!

왼쪽) 이런 모양으로 기존 사용하려는 함수를 감싸는 꼴이다.  / 오른쪽) decorator 사용

대문자로 바꿔주는 decorator

다중 Decorator 적용 순서는 아래에서 위로 쭉

다중 decorator
인자받기

wrapper 클로저의 정의에서 * 및 ** 연산자를 사용하여서 모든 위치 및 키워드 인자를 수집하고 변수에 저장한다.

wrapper 클로저는 수집된 인자를 * 및 ** 인자 풀기 연산자를 사용하여서 입력 함수에 전달한다. 

데코레이터를 사용해서 디버깅을 어떻게 하면 될까?

데코레이터를 사용하면 실제로 한 함수가 다른 함수로 교체되기 때문에 한 가지 단점은 일부 메타데이터를 숨겨버린다는 것이다. 여기서는 docstring이 사라진 것을 볼 수 있다.

이로 인해 디버깅이 어려워질 수 있는데, 이것의 해결책은 functools.wraps 데코레이터를 사용하면 된다.

functools.wrap을 사용해서 파이썬 디버깅을 잘해보자!

그렇다고 너무 남발하지는 말자! 결국 디버깅을 잘하려고 하는 건데 남발해서 디버깅이 더 어려워질 수도 있다.

한 가지 예로 함수 로딩 시간을 체크하는 간단한 함수를 찾아보고 변형을 해봤다.

import time 
def logging_time(func):
    @functools.wraps(func)
    def wrapper_fn(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print("Time [{}]: {} sec".format(func.__name__, end_time-start_time))
        return print(result)
    return wrapper_fn

@logging_time
def ck(*args , **kwargs) :
    """check user"""
    if kwargs["name"] in ["sr", "lr"] : return "check!"
    else : return "not user"

@logging_time
def say(name, job):
    """say function"""
    ck(name = name)
    return f"{name} , {job}"
say.__doc__
## `say function`

이렇게 하면 될 것 같긴 한데 각 함수 별로 특정 지표값을 남기기가 어려운 것 같다. 그래서 좀 바꿔보니 이렇게 하면 될 것 같다.

import time 
def logging_time(func):
    @functools.wraps(func)
    def wrapper_fn(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(kwargs["logging"])
        print("Time [{}]: {} sec".format(func.__name__, end_time-start_time))
        return print(result)
    return wrapper_fn

@logging_time
def ck(*args , **kwargs) :
    """check user"""
    if kwargs["name"] in ["sr", "lr"] : return "check!"
    else : return "not user"

@logging_time
def say(*args ,  **kwargs):
    """say function"""
    ck(**kwargs)
    return "{} , {}".format(kwargs['name'] , kwargs['job'])

@logging_time
def say2(*args ,  **kwargs):
    """say function"""
    return "{} , {}".format(kwargs['age'] , kwargs['married'])

logging이라는 인자를 줘서 거기에 표시하게 하면 쉽게 분리할 수 있는 표시를 만들 수 있을 것 같다. 

728x90