본문 바로가기
Python Basic

decorator 사용법

by fiasco 2022. 11. 23.

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

 

파이썬 코딩 도장: 42.1 데코레이터 만들기

Unit 42. 데코레이터 사용하기 파이썬은 데코레이터(decorator)라는 기능을 제공합니다. 데코레이터는 장식하다, 꾸미다라는 뜻의 decorate에 er(or)을 붙인 말인데 장식하는 도구 정도로 설명할 수 있습

dojang.io

클로저 사용법 : https://fiasco-at-python.tistory.com/46

 

0. 데커레이터 도입 개념

# 함수 출력 결과 확인 

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


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


hello()
world()


# closure를 이용한 함수 호출

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()                 # 반환된 함수를 호출

 

1. 데커레이터
# 데커레이터 원리

def trace(func):                           # 호출할 함수를 매개변수로 받음
    def wrapper():                         # 호출할 함수를 감싸는 함수
        print(func.__name__, '함수 시작')  # __name__으로 함수 이름 출력
        func()                             # 매개변수로 받은 함수를 호출
        print(func.__name__, '함수 끝')

    return wrapper                         # wrapper 함수 반환

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

hello()

 

1.1 함수 데커레이터

# 여러개의 데코레이터 지정

def decorator1(func):
    def wrapper():
        print('decorator1')
        func()

    return wrapper


def decorator2(func):
    def wrapper():
        print('decorator2')
        func()

    return wrapper
    

@decorator1
@decorator2
def hello():
    print('hello')
    
hello()

 

# 데커리터 미사용시 위와 동일
def hello():
    print('hello')
    
result=decorator1(decorator2(hello))
result()

 

# 매개변수와 반환값을 출력하는 데코레이터

def trace(func):        # 호출할 함수를 매개변수로 받음
    def wrapper(a, b):  # 호출할 함수 add(a, b)의 매개변수와 똑같이 지정
        r = func(a, b)  # func에 매개변수 a, b를 넣어서 호출하고 반환값을 변수에 저장
        print('{0}(a={1}, b={2}) -> {3}'.format(func.__name__, a, b, r))  # 매개변수와 반환값 출력
        return r        # func의 반환값을 반환

    return wrapper  # wrapper 함수 반환


@trace            # @데코레이터
def add(a, b):    # 매개변수는 두 개
    return a + b  # 매개변수 두 개를 더해서 반환


print(add(10, 20))

 

# 데커레이터 미사용
def add(a, b):    # 매개변수는 두 개
    return a + b  # 매개변수 두 개를 더해서 반환


result = trace(add)
print(result(10,20))


# 가변 인수 함수 데코레이터

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

    return wrapper                  # wrapper 함수 반환

@trace  # @데코레이터
def get_max(*args,**kwargs):
    result1 = max(args)
    result2 = max(kwargs.values())
    result = max(result1,result2)
    return result

print(get_max(10, 20,30,a=15))


# 메서드에 데코레이터 사용하기
클래스를 만들면서 메서드에 데코레이터를 사용할 때는 self를 주의해야 합니다. 
인스턴스 메서드는 항상 self를 받으므로 데코레이터를 만들 때도 wrapper 함수의 첫 번째 매개변수는 self로 지정해야 합니다(클래스 메서드는 cls). 
마찬가지로 func를 호출할 때도 self와 매개변수를 그대로 넣어야 합니다.

def trace(func):
    def wrapper(self, a, b):  # 호출할 함수가 인스턴스 메서드이므로 첫 번째 매개변수는 self로 지정
        r = func(self, a, b)  # self와 매개변수를 그대로 넣어줌
        print('{0}(a={1}, b={2}) -> {3}'.format(func.__name__, a, b, r))
        return r

    return wrapper


class Calc:
    @trace
    def add(self, a, b):  # add는 인스턴스 메서드
        return a + b


c = Calc()
print(c.add(10, 20))


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

def is_multiple(x):            # 데코레이터가 사용할 매개변수를 지정
    def real_decorator(func):  # 호출할 함수를 매개변수로 받음
        def wrapper(a, b):     # 호출할 함수의 매개변수와 똑같이 지정
            r = func(a, b)     # func를 호출하고 반환값을 변수에 저장
            if r % x == 0:     # func의 반환값이 x의 배수인지 확인
                print('{0}의 반환값은 {1}의 배수입니다.'.format(func.__name__, x))
            else:
                print('{0}의 반환값은 {1}의 배수가 아닙니다.'.format(func.__name__, x))
            return r  # func의 반환값을 반환
        return wrapper  # wrapper 함수 반환
    return real_decorator  # real_decorator 함수 반환

@is_multiple(3)  # @데코레이터(인수)
def add(a, b):
    return a + b


print(add(10, 20))
print(add(2, 5))


# 매개변수가 있는 데코레이터를 여러 개 지정하기

import functools
 
def is_multiple(x):
    def real_decorator(func):
        @functools.wraps(func)    # @functools.wraps에 func를 넣은 뒤 wrapper 함수 위에 지정
        def wrapper(a, b):
            r = func(a, b)
            if r % x == 0:
                print('{0}의 반환값은 {1}의 배수입니다.'.format(func.__name__, x))
            else:
                print('{0}의 반환값은 {1}의 배수가 아닙니다.'.format(func.__name__, x))
            return r
        return wrapper
    return real_decorator
 
@is_multiple(3)
@is_multiple(7)
def add(a, b):
    return a + b
 
add(10, 20)

'''
# 동일 결과
decorated_add = is_multiple(3)(is_multiple(7)(add))
decorated_add(10, 20)
''''

 

1.2 클래스 데커레이터

# class 콜러를 이용한 데커레이터
# 클래스에 __call__ 메서드를 정의했으므로 함수처럼 ( )(괄호)를 붙여서 호출할 수 있습니다.

class Trace:
    def __init__(self, func):  # 호출할 함수를 인스턴스의 초깃값으로 받음
        self.func = func       # 호출할 함수를 속성 func에 저장

    def __call__(self):
        print(self.func.__name__, '함수 시작')  # __name__으로 함수 이름 출력
        self.func()                             # 속성 func에 저장된 함수를 호출
        print(self.func.__name__, '함수 끝')


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


hello()  # 함수를 그대로 호출

 

# 데커레이터 미사용 시
def hello():
    print('hello')


trace_hello = Trace(hello)    # 데코레이터에 호출할 함수를 넣어서 인스턴스 생성
trace_hello()                 # 인스턴스를 호출. __call__ 메서드가 호출됨


# 매개변수와 반환값을 처리하는 클래스로 만든 데코레이터

class Trace:
    def __init__(self, func):    # 호출할 함수를 인스턴스의 초깃값으로 받음
        self.func = func         # 호출할 함수를 속성 func에 저장
 
    def __call__(self, *args, **kwargs):    # 호출할 함수의 매개변수를 처리
        r = self.func(*args, **kwargs) # self.func에 매개변수를 넣어서 호출하고 반환값을 변수에 저장
        print('{0}(args={1}, kwargs={2}) -> {3}'.format(self.func.__name__, args, kwargs, r))
                                            # 매개변수와 반환값 출력
        return r                            # self.func의 반환값을 반환
 
@Trace    # @데코레이터
def add(a, b):
    return a + b
 
print(add(10, 20))
print(add(a=10, b=20))


# 클래스로 매개변수가 있는 데코레이터 만들기

class IsMultiple:
    def __init__(self, x):         # 데코레이터가 사용할 매개변수를 초깃값으로 받음
        self.x = x                 # 매개변수를 속성 x에 저장
 
    def __call__(self, func):      # 호출할 함수를 매개변수로 받음
        def wrapper(a, b):         # 호출할 함수의 매개변수와 똑같이 지정(가변 인수로 작성해도 됨)
            r = func(a, b)         # func를 호출하고 반환값을 변수에 저장
            if r % self.x == 0:    # func의 반환값이 self.x의 배수인지 확인
                print('{0}의 반환값은 {1}의 배수입니다.'.format(func.__name__, self.x))
            else:
                print('{0}의 반환값은 {1}의 배수가 아닙니다.'.format(func.__name__, self.x))
            return r               # func의 반환값을 반환
        return wrapper             # wrapper 함수 반환
 
@IsMultiple(3)    # 데코레이터(인수)
def add(a, b):
    return a + b
 
print(add(10, 20))

'Python Basic' 카테고리의 다른 글

이터레이터(iterator)  (0) 2022.11.23
Exception  (0) 2022.11.23
closure 사용법  (0) 2022.11.23
Programming productivity tools  (0) 2022.11.08
Debugging  (0) 2022.11.08