우와한 개발자

[Python] 데코레이터(Decorator) - @문법, functools.wraps

by 우와한개발자

1. 데코레이터란?

  • '장식하는 사람'이라는 뜻처럼, 함수를 직접 수정하지 않고 기능을 추가할 때 사용
  • 기존 함수를 입력받아 기능이 추가된 새로운 함수 객체로 만들어주는 역할
  • AOP(관점 지향 프로그래밍)의 파이썬 구현 방식
  • 로깅, 인증, 실행시간 측정 등 공통 기능을 분리할 때 활용

 

2. 기본 구조

def my_decorator(func):      # 1. 원본 함수를 인자로 받음
    def wrapper():           # 2. 기능을 추가한 새 함수 정의
        print('실행 전')
        func()               # 3. 원본 함수 호출
        print('실행 후')
    return wrapper           # 4. wrapper 함수 반환 (호출 X)

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

hello()
# 실행 전
# hello
# 실행 후

 

3. 데코레이터 적용 방법

1) 직접 할당

hello = my_decorator(hello)   # hello 변수를 기능이 추가된 함수로 대체
hello()

# 실행 전
# hello
# 실행 후

 

2) @ 문법

  • hello = my_decorator(hello) 와 완전히 동일한 동작
  • 함수 정의 위에 @데코레이터함수 를 붙여주면 됨
@my_decorator
def hello():
    print('hello')

hello()

# 실행 전
# hello
# 실행 후

 

4. 인자가 있는 함수에 적용

  • *args, **kwargs 로 인자를 그대로 전달
from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print('실행 전')
        result = func(*args, **kwargs)    # 인자 그대로 전달
        print('실행 후')
        return result
    return wrapper

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

print(add(1, 2))
# 실행 전
# 실행 후
# 3

 

5. 데코레이터 여러 개 적용

  • 위에서 아래 순서로 감싸고, 실행은 위에서부터
def decorator_a(func):
    def wrapper():
        print('A 실행 전')
        func()
        print('A 실행 후')
    return wrapper

def decorator_b(func):
    def wrapper():
        print('B 실행 전')
        func()
        print('B 실행 후')
    return wrapper

@decorator_a
@decorator_b
def hello():
    print('hello')

hello()
# A 실행 전
# B 실행 전
# hello
# B 실행 후
# A 실행 후

 

6. functools.wraps

  • 데코레이터 적용 시 함수 이름과 docstring 이 wrapper로 덮어씌워지는 문제 방지
  • 원래 @my_decorator 를 붙이는 순간 hello = my_decorator(hello) 가 되고, 결국 hello 는 wrapper 함수가 되어버리기 때문에 이름도 wrapper가되고 docstring도 None 이 되는데 그걸 방지해줌
from functools import wraps

def my_decorator(func):
    @wraps(func)            # ← func의 이름, docstring을 wrapper에 복사
    def wrapper():
        print('실행 전')
        func()
        print('실행 후')
    return wrapper

@my_decorator
def hello():
    """인사 함수"""
    print('hello')

print(hello.__name__)   # hello   
print(hello.__doc__)    # 인사 함수

 

7. 실전 활용 예시

1) 실행시간 측정

import time
from functools import wraps

def timer(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f'{func.__name__} 실행시간: {end - start:.4f}초')
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(1)

slow_function()
# slow_function 실행시간: 1.0012초

 

2) 로그 출력

from functools import wraps

def logger(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f'[LOG] {func.__name__} 호출 - args: {args}, kwargs: {kwargs}')
        return func(*args, **kwargs)
    return wrapper

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

add(1, 2)
# [LOG] add 호출 - args: (1, 2), kwargs: {}

블로그의 정보

우와한개발자 님의 블로그

우와한개발자

활동하기