본문 바로가기
C.W.K.
Stream
Lesson 02 of 05 · published

functools.wraps — 감싼 뒤에도 원래 함수로 보이게

~15 min · wraps, functools, metadata, introspection

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

래퍼는 원래 함수의 이름을 가릴 수 있어

그냥 감싸면 __name__, 문서 문자열, 어노테이션, 시그니처가 래퍼의 것으로 보여. 디버거와 문서 도구, inspect.signature는 원래 계약을 잃어버려.

wraps가 메타데이터와 통로를 남긴다

래퍼에 @functools.wraps(fn)를 붙이면 중요한 메타데이터를 복사하고 __wrapped__로 원래 함수를 가리켜. 여러 데코레이터가 겹쳐도 검사 도구가 안쪽 함수까지 따라갈 수 있어.

원칙: 함수를 감싸는 모든 데코레이터는 특별한 이유가 없으면 wraps를 기본으로 써.

Code

wraps 없이 잃어버리는 함수 정보·python
def announce(fn):
    def wrapper(*args, **kwargs):
        """이건 wrapper 의 docstring."""
        print("calling", fn.__name__)
        return fn(*args, **kwargs)
    return wrapper

@announce
def hello(name):
    """이름으로 인사."""
    return f"hi {name}"

print(hello.__name__)        # 'wrapper'   <- 잃음
print(hello.__doc__)         # '이건 wrapper 의 docstring.'   <- 잃음
functools.wraps로 메타데이터 보존하기·python
import functools

def announce(fn):
    @functools.wraps(fn)
    def wrapper(*args, **kwargs):
        print("calling", fn.__name__)
        return fn(*args, **kwargs)
    return wrapper

@announce
def hello(name):
    """이름으로 인사."""
    return f"hi {name}"

print(hello.__name__)        # 'hello'      ✓
print(hello.__doc__)         # '이름으로 인사.'   ✓

# 보너스 — __wrapped__ 가 원래 함수 가리킴
print(hello.__wrapped__("pippa"))   # 'hi pippa'  — wrapper 우회
inspect.signature가 원래 계약 읽기·python
import functools
import inspect

def trace(fn):
    @functools.wraps(fn)
    def wrapper(*args, **kwargs):
        return fn(*args, **kwargs)
    return wrapper

@trace
def compute(x: int, y: int = 5) -> int:
    """x + y 반환."""
    return x + y

# wraps + __wrapped__ 덕분에 시그니처 정확
print(inspect.signature(compute))    # (x: int, y: int = 5) -> int
print(compute.__doc__)               # 'x + y 반환.'
print(compute.__annotations__)       # {'x': int, 'y': int, 'return': int}
데코레이터의 기본 뼈대·python
import functools

def my_decorator(fn):
    @functools.wraps(fn)               # <- 항상 이거
    def wrapper(*args, **kwargs):
        # ... 호출 전 로직 ...
        result = fn(*args, **kwargs)
        # ... 호출 후 로직 ...
        return result
    return wrapper

External links

Exercise

앞 수업의 count_callsfunctools.wraps를 붙여 __name__, __doc__, inspect.signature, __wrapped__가 원래 함수를 보존하는지 확인해. wraps가 없는 버전도 만들어 repr과 help 출력이 어떻게 달라지는지 비교해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.