Python

[파이썬 코드 업] 4-3장. 데코레이터

patrick-star 2023. 5. 6. 10:21
728x90

4.9 데코레이터

파이썬에서 제공하는 데코레이터는 함수를 장식한다는 뜻에서 붙여진 이름이다. 그렇다면 언제, 어떻게 사용해야 할까??

데코레이터는 함수를 수정하지 않은 상태에서 추가 기능을 구현할 때 사용한다.

개념 및 예시

ex) hello 함수, world 함수

  • 기존함수 hello, world
def hello() : 
    print('hello')


def world() : 
    print('world')

이때, 각각의 함수의 시작과 끝을 출력하는 코드를 넣고 싶다. 그러면 아래와 같이 함수를 수정해주면 된다.

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


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

하지만, 위와 같이 일일이 함수를 넣어준다면 전체적인 코드도 길어지고 각각의 함수에 일일이 넣어줘야 한다는 불편함이 있다.

이럴 때 데코레이터를 활용하면 편리하게 사용할 수 있다.

def trace(func) : 
    def wrapper() : # 함수를 감싼다고 해서 wrapper. 이름을 다른 거 써도 상관없음 
        print(func.__name__, '함수 시작')
        func()
        print(func.__name__, '함수 끝')
    return wrapper

def hello() : 
    print('hello')


def world() : 
    print('world')

trace_hello = trace(hello) # 데코레이터에 호출할 함수(hello)를 넣음 
trace_hello()

trace_world = trace(world) # 데코레이터에 호출할 함수(world)를 넣음 
trace_world() 

정리하면, 원래는 함수의 시작과 끝에 내용을 출력하려면 함수를 직접 수정해야 했지만

그 과정이 너무 번거로우니까

함수의 시작과 끝을 출력하도록 하는 데코레이터 함수를 만들어서
시작과 끝을 출력하고 싶은 함수를 데코레이터 함수의 매개변수로 넣어주기만 하면 되도록 했다.

ex) 함수 실행 시간을 측정하는 데코레이터
(매개변수로 입력되는 함수에 매개변수가 필요할 수 있기 때문에 *args, **kwargs를 추가함)

def make_timer(func) :
    def wrapper(*args, **kwargs) : 
        t1 = time.time()
        ret_val = func(*args, **kwargs)
        t2 = time.time()

        print('소요 시간 : ' , t2- t1) # 원하는 문장 

        return ret_val
    return wrapper

def hello() : 
    print('hello')


def world() : 
    print('world')

hello_time = make_timer(hello) # 데코레이터에 호출할 함수(hello)를 넣음 
hello_time()

world_time = make_timer(world) # 데코레이터에 호출할 함수(world)를 넣음 
world_time()

마찬가지로 각각의 함수의 실행 시간을 측정하는 코드를 넣는게 번거로우니까

실행 시간을 측정할 수 있는 데코레이터 함수(make_timer)를 만들어서
실행 시간을 측정하고 싶은 함수를 데코레이터 함수의 매개변수로 넣어주기만 하면 되도록 했다.

@를 사용한 데코레이터

  • 앞서 소개한 방식
def trace(func) : 
    def wrapper() : # 함수를 감싼다고 해서 wrapper. 이름을 다른 거 써도 상관없음 
        print(func.__name__, '함수 시작')
        func()
        print(func.__name__, '함수 끝')
    return wrapper

def hello() : 
    print('hello')


def world() : 
    print('world')

trace_hello = trace(hello) # 데코레이터에 호출할 함수(hello)를 넣음 
trace_hello()

trace_world = trace(world) # 데코레이터에 호출할 함수(world)를 넣음 
trace_world() 
  • @을 사용한 방식
def trace(func) : 
    def wrapper() : # 함수를 감싼다고 해서 wrapper. 이름을 다른 거 써도 상관없음 
        print(func.__name__, '함수 시작')
        func()
        print(func.__name__, '함수 끝')
    return wrapper

@trace # trace라는 데코레이터를 이용한 호출을 원하는 함수에 붙여준다. 
def hello() : 
    print('hello')

@trace # trace라는 데코레이터를 이용한 호출을 원하는 함수에 붙여준다. 
def world() : 
    print('world')

# 두 개의 함수는 @trace를 붙였으니까
# trace 데코레이터를 이용해서 호출된다. 

hello() # trace_hello = trace(hello) 
        # trace_hello() 와 동일한 동작

world() # trace_world = trace(world) 
        # trace_world() 와 동일한 동작

이와 같이 데코레이터는 함수를 감싸는 형태로 구성되어 있어서 기존 함수를 수정하지 않으면서 추가 기능을 구현할 때 사용한다.

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