IT/파이썬

[코딩도장] day28. 파이썬 데코레이터(1/2) - @데코레이터, def데코레이터

_하늘여우_ 2020. 9. 22. 00:02

출처 : unsplash

 

■ 들어가기

- 파이썬 코딩 도장 (남재윤/길벗). 을 공부하며 정리하는 블로그


▶ Unit42. 데코레이터 사용하기

     - 42.1 데코레이터 만들기

     - 42.2 매개변수와 반환값을 처리하는 데코레이터 만들기

     - 42.3 매개변수가 있는 데코레이터 만들기

     - 42.4 클래스로 데코레이터 만들기

     - 42.5 클래스로 매개변수와 반환값을 처리하는 데코레이터 만들기

 

 

0. 들어가기

- 데코레이터(decorator) : 클래스 내 메서드를 장식하는 도구 (표기: @)

- 사용예시

class Calc :

    @staticmethod   # 데코레이터

    def add(a, b) :

        print(a + b)

 

 

>> 42.1 데코레이터 만들기 <<

- 데코레이터는 함수를 수정하지 않은 상태에서 추가 기능 구현 시 사용

- 예제 : 함수 시작, 끝을 출력하는 기능 구현

설명 : 일반적인 함수 작성
소스 (function_begin_end.py) 결과
def hello() :
    print('hello 함수 시작')
    print('hello')
    print('hello 함수 끝')

def world() :
    print('world 함수 시작')
    print('world')
    print('world 함수 끝')

hello()
world()
hello 함수 시작
hello
hello 함수 끝
world 함수 시작
world
world 함수 끝

 

설명 : 데코레이터를 활용한 함수  
소스 (decorator_closure.py) 결과
def trace(func) :                           # 호출할 함수를 매개변수로 받음
    def wrapper() :                         # 호출할 함수를 감싸는 함수
        print(func.__name__, '함수 시작')   # __name__으로 함수 이름 출력
        func()                              # 매개변수로 받은 함수를 호출
        print(func.__name__, '함수 끝')
    return wrapper                          # wrapper 함수 반환

def hello() :
    print('hello')

def world() :
    print('world')

trace_hello = trace(hello)  # 데코레이터에 호출할 함수를 넣음
trace_hello()               # 반환된 함수를 호출
trace_world = trace(world)  # 데코레이터에 호출할 함수를 넣음
trace_world()               # 반환된 함수를 호출
hello 함수 시작
hello
hello 함수 끝
world 함수 시작
world
world 함수 끝

 

1. @으로 데코레이터 사용하기

- 사용법

@데코레이터

def 함수이름() :

    코드

소스 (decorator_closure_at_sign.py) 결과
def trace(func) :   # 호출할 함수를 매개변수로 받음
    def wrapper() :
        print(func.__name__, '함수 시작')   # __name__으로 함수 이름 출력
        func()                              # 매개변수로 받은 함수를 호출
        print(func.__name__, '함수 끝')
    return wrapper

@trace  # @데코레이터
def hello() :
    print('hello')

@trace  # @데코레이터
def world() :
    print('world')

hello()     # 함수를 그대로 호출
world()     # 함수를 그대로 호출
hello 함수 시작
hello
hello 함수 끝
world 함수 시작
world
world 함수 끝

출처 : 파이썬 코딩 도장 (dojang.io)

- 그러면 trace라는 함수말고 다른 이름의 함수 만들고 해당 함수 이름으로 @ 데코레이터 붙히면 어떻게 될까??

- 결론 : 된다!!!! // 데코레이터 이름의 함수를 만들고 데코레이터로 호출하면 처리 가능하구나!! 그렇지 않으면 NameError 발생!

소스 (decorator_closure_at_sign2.py) 결과
def abc(func) :   # 호출할 함수를 매개변수로 받음
    def wrapper() :
        print(func.__name__, '함수 시작')   # __name__으로 함수 이름 출력
        func()                              # 매개변수로 받은 함수를 호출
        print(func.__name__, '함수 끝')
    return wrapper

@abc  # @데코레이터
def hello() :
    print('hello')

@abc  # @데코레이터
def world() :
    print('world')

hello()     # 함수를 그대로 호출
world()     # 함수를 그대로 호출
hello 함수 시작
hello
hello 함수 끝
world 함수 시작
world
world 함수 끝

 

 

>> 42.2 매개변수와 반환값을 처리하는 데코레이터 만들기 <<

- 매개변수와 반환값을 처리하는 데코레이터 확인

- 실제 호출할 함수의 매개변수와 wrapper 함수의 매개변수를 똑같이 만들어줌

소스 (decorator_param_return.py) 결과
def trace(func) :
    def wrapper(a, b) :
        r = func(a, b)
        print('{0}(a={1}, b={2}) -> {3}'.format(func.__name__, a, b, r))
        return r
    return wrapper

@trace
def add(a, b) :
    return a + b

print(add(10, 20))
add(a=10, b=20) -> 30
30

 

 

1. 가변 인수 함수 데코레이터

- 매개변수(인수)가 고정되지 않은 함수 처리 방법은?

-> wrapper 함수를 가변 인수 함수로 만들면 된다.

소스 (decorator_variable_argument.py) 결과
def trace(func) :   # 호출할 함수를 매개변수로 받음
    def wrapper(*args, **kwargs) :  # 가변 인수 함수로 만듦
        r = func(*args, **kwargs)   # func에 args, kwrargs를 언패킹하여 넣어줌
        print('{0}(args={1}, kwargs={2}) -> {3}'.format(func.__name__, args, kwargs, r)) # 매개변수와 반환값 출력
        return r                    # func의 반환값을 반환
    return wrapper                  # wrapper 함수 반환

@trace  # @데코레이터
def get_max(*args) :    # 위치 인수를 사용하는 가변 인수 함수
    return max(args)

@trace  # @데코레이터
def get_min(**kwargs) : # 키워드 인수를 사용하는 가변 인수 함수
    return min(kwargs.values())

print(get_max(10, 20))
print(get_min(x=10, y=20, z=30))
get_max(args=(10, 20), kwargs={}) -> 20
20
get_min(args=(), kwargs={'x': 10, 'y': 20, 'z': 30}) -> 10
10

- get_max, get_min 함수는 가변 인수 함수로써, get_max 함수는 튜플로, get_min 함수는 딕셔너리(key-value)로 데이터 전달

- args 는 튜플, kwargs는 딕셔너리