Programming/Python

[Python] decorator - 인자 받기, 중첩 사용, wraps 이해하기

아카카 2025. 6. 8. 12:44

 

2025.06.07 - [Python/입문] - [Python] decorator 사용법 - 함수 위에 @가 붙는 이유?

 

[Python] decorator 사용법 - 함수 위에 @가 붙는 이유?

파이썬 코드를 보다보면 함수위에 '@'기호가 붙어있는 걸 볼 수 있습니다.@somethingdef my_function(): pass해당 기능은 데코레이터(decorator) 문법입니다.데코레이터는 기존 함수를 수정하지 않고도 함수

dev-kjs1219.tistory.com

 

지난 포스팅에 이어서 decorator를 더 잘 사용할 수 있는 방법에 대해 정리해보았습니다.

  • decorator에 매개변수를 전달하는 방법
  • decorator를 중첩해서 사용하는 방법
  • 원래 함수 정보 유지를 위한 functools.wraps 의 역할

1. 매개변수를 받는 decorator

decorator는 아래와 같은 형태로 사용됩니다.

@simple_decorator
def func():
	...

하지만 decorator에도 매개변수를 넘겨주고 사용하고 싶을때는 아래와 같이 사용할 수 있습니다.

@simple_decorator(n=10)
def func():
	...

위와 같이 사용할 수 있는 구현 예제는 아래와 같습니다.

def repeat(n):
	def decorator(func):
    		def wrapper(*args, **kwargs) :
                for i in range(n):
                    func(*args, **kwargs)
    		return wrapper
    return decorator
    
@repeat(3)
def greet():
	print("Hello!")

greet()

#Hello!
#Hello!
#Hello!

이 예제는 데코레이터에 인자를 전달하는 방법을 보여줍니다.
repeat 함수는 n이라는 매개변수를 받아 내부에 데코레이터 함수를 생성하고,
그 데코레이터는 전달받은 함수를 n번 반복해서 실행합니다.

이처럼 데코레이터에 인자를 전달하려면
"데코레이터를 반환하는 함수를 한 번 더 감싸는 구조"로 작성해야 합니다.
그 결과 @repeat(3)은 greet() 함수를 3번 실행하게 됩니다.

 

2. decorator 중첩사용

import time

def logger(func):
    def wrapper(*args, **kwargs):
        print(f"[LOG] {func.__name__} 호출됨")
        return func(*args, **kwargs)
    return wrapper

def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"[TIMER] 실행 시간: {end - start:.4f}초")
        return result
    return wrapper

@timer
@logger
def process():
    print("작업 수행 중...")

process()

#[LOG] process 호출됨
#작업 수행 중...
#[TIMER] 실행 시간: 0.0000초

이 예제에서는 데코레이터를 두 개 중첩하여 사용하는 방법을 보여주고 있습니다.
@logger가 먼저 process() 함수를 감싸고, 그 결과를 @timer가 다시 한 번 감싸는 구조입니다.

따라서 process()를 호출하면,

  • 먼저 logger가 로그를 출력하고
  • 그 후 timer가 시간을 측정하며 실행 시간을 출력합니다.

여러 데코레이터를 적용할 경우, 데코레이터는 아래에서부터 위로 순차적으로 감싸진다는 점을 기억해야 합니다.
이 순서에 따라 실행 흐름이나 출력 결과가 달라질 수 있습니다.

 

3. functools.wraps로 함수 정보 유지

from functools import wraps

def deco(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@deco
def test():
    """테스트 함수입니다."""
    pass

print(test.__name__)  # test
print(test.__doc__)   # 테스트 함수입니다.

데코레이터를 사용하면 __name__, __doc__ 등의 함수 정보가
감싸진 내부 함수(wrapper)로 덮여쓰기 되어 버립니다.
따라서 원래 함수 이름이나 주석(docstring)을 참조하려 할 때 문제가 발생합니다.

이 문제를 해결하기 위해 파이썬에서는 functools 모듈에서 제공하는
@wraps(func) 데코레이터를 함께 사용합니다.

@wraps(func)는 내부 wrapper 함수에 원래 함수의 메타 정보를 그대로 복사해 주는 역할을 합니다.
이렇게 하면 데코레이터를 적용한 후에도 함수의 이름이나 설명 등이 정상적으로 유지됩니다.