본문 바로가기
👩🏻‍💻 Computer Science/clean-code(ft.python)

Chapter5. 고급 데코레이터(기본값을 가진 데코레이터)

by devhertz 2026. 9. 13.

기본값을 가진 데코레이터

  • 데코레이터에 기본값을 주는 이유는 사용자가 파라미터를 깜빡 잊고 안 넘겨도 동작하게 하기 위해서이다.
# 기본값 사용
@WithRetry()
@WithRetry(retries_limit=5)

# 괄호없이 사용하면 문제 발생
@WithRetry                
def my_function():
    ...
  • 문제가 발생하는 이유
    • @WithRetry()WithRetry()를 먼저 호출해서 인스턴스를 만들고, 그 인스턴스가 함수를 받음
    • @WithRetryWithRetry 클래스 자체가 함수를 바로 받게 됨 (즉 __init__이 아니라 클래스가 직접 호출되는 상황이 되어 버림)
  • 괄호가 없는 경우는 첫 번째 파라미터로 함수가 전달되지만, 괄호가 있는 경우는 첫 번째 파라미터로 None이 전달된다.
  • 문서상으로는 괄호를 사용하는 방식@retry())이 올바른 방식

 

두 가지 호출 방식(괄호 있음/없음)을 모두 지원하는 방법

 

1. 가장 간단한 방법: 첫 번째 파라미터가 None인지 확인

def decorator(function=None, *, x=DEFAULT_X, y=DEFAULT_Y):
    if function is None:
        # @decorator(x=3, y=4) 형태로 호출된 경우
        def decorated(function):
            @wraps(function)
            def wrapped():
                return function(x, y)
            return wrapped
        return decorated
    else:
        # @decorator 형태로 괄호 없이 호출된 경우
        @wraps(function)
        def wrapped():
            return function(x, y)
        return wrapped

 

2. 더 깔끔한 방법 (lambda 또는 partial 사용):

from functools import partial

def decorator(function=None, *, x=DEFAULT_X, y=DEFAULT_Y):
    if function is None:
        return lambda f: decorator(f, x=x, y=y)
        # 또는
        # return partial(decorator, x=x, y=y)

    @wraps(function)
    def wrapped():
        return function(x, y)
    return wrapped
  • 함수가 안 넘어오면 → “나중에 함수를 받겠다”는 의미의 함수를 반환
  • 함수가 넘어오면 → 바로 래핑해서 반환
TIPS:파라미터를 가진 데코레이터를 정의할 때는 키워드 전용 파라미터를 사용하도록 하자.
def with_retry(*, retries_limit=3, allowed_exceptions=None):  # * 뒤에 오는 것은 키워드 전용
    ...
  • 위치 인자로 넘기면 의미가 불명확해짐
  • 키워드로 강제하면 코드의 의도가 명확해짐

파라미터가 아예 없는 데코레이터라면 다음과 같이 명시할 수도 있음

def retry(operation, /):   # / 앞은 위치 전용
    ...
  • 하지만 필수는 아니고, 호출 방식을 명시적으로 표현하고 싶을 때 사용하는 방법

 

댓글