노트

파이썬 데코레이터

Python Decorators

CS#python#pattern · 연결된 개념 7개

쉽게 말하면

파이썬 데코레이터는 선물에 포장지를 씌우듯 함수를 감싸서, 원래 내용은 그대로 두고 앞뒤에 로깅·권한 검사·캐시를 덧붙이는 문법이에요. 함수 위에 @ 한 줄만 쓰면 돼요.

비유가 깨지는 곳 포장지와 달리 감싸면 겉에서 보이는 이름이 바뀌어요. functools.wraps를 빼면 함수 이름이 wrapper가 되어 디버깅이 헷갈리고, @deco는 사실 func = deco(func) 대입이에요.

파이썬 데코레이터는 함수(또는 클래스)를 받아 다른 호출 가능한 객체를 돌려주는 함수다. @deco를 함수 정의 위에 쓰면 func = deco(func)와 같다. 원래 함수 앞뒤에 로깅·권한 검사·캐시 같은 기능을 덧붙일 때 쓴다.

import functools, logging, time
 
def timed(func):
    @functools.wraps(func)                 # 원래 이름·docstring 보존
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        try:
            return func(*args, **kwargs)
        finally:
            logging.info("%s %.1fms", func.__name__, (time.perf_counter() - start) * 1000)
    return wrapper
 
@timed
def fetch(user_id): ...
  • 함수가 일급 객체(first-class object)라서 가능하다. 함수를 인자로 받고, 안에서 함수를 만들어 돌려준다(클로저)
  • functools.wraps를 빼면 감싼 함수의 이름이 wrapper가 되어 디버깅·문서화가 헷갈린다
  • *args, **kwargs로 받아 그대로 넘기면 어떤 시그니처의 함수에도 쓸 수 있다(*args·**kwargs와 언패킹)

인자를 받는 데코레이터

def retry(times):
    def deco(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for i in range(times):
                try:
                    return func(*args, **kwargs)
                except ConnectionError:
                    if i == times - 1:
                        raise
        return wrapper
    return deco
 
@retry(times=3)
def call_api(): ...

클래스로 만들기

__call__을 구현한 클래스의 인스턴스도 호출할 수 있으므로 데코레이터가 될 수 있다. 상태(호출 횟수 등)를 들고 있어야 할 때 쓴다. 하지만 보통은 함수로 충분하다.

자주 보는 예

  • 표준 라이브러리: @property, @classmethod, @staticmethod, @functools.cache, @dataclass
  • 장고: @login_required, @transaction.atomic
  • 디버깅: 인자와 반환값을 찍는 데코레이터를 잠깐 붙였다 뗀다. 함수 안에서는 vars()로 지역 변수를 한 번에 보고, breakpoint()로 디버거를 연다

감싸서 기능을 더하는 구조는 데코레이터 패턴과 같고, 스프링에서는 AOP가 프록시로 같은 일을 한다. 타입스크립트의 데코레이터는 문법은 비슷해도 동작 시점이 다르다(TypeScript 데코레이터).

출처: Python 용어집: decorator · functools.wraps · PEP 318

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • 가변 기본 인자 함정

    파이썬 함수의 기본값은 함수를 정의할 때 한 번 평가되어 그 객체가 계속 재사용된다. 그래서 리스트·딕셔너리 같은 변경 가능한 객체를 기본값으로 쓰면, 호출할 때마다 같은 객체가 공유되어 값이 쌓인다.

  • 파이썬 기본 컬렉션

    파이썬의 기본 컬렉션은 리스트·튜플·딕셔너리·셋 네 가지다. 가장 중요한 구분은 변경 가능 여부(mutable·immutable)와 순서·중복을 어떻게 다루는가다.

  • 이터레이터와 제너레이터

    이터레이터(iterator)는 __next__()로 값을 하나씩 내주다가 끝나면 StopIteration을 던지는 객체다. 제너레이터(generator)는 yield를 쓴 함수로, 이터레이터를 클래스 없이 간단히 만드는 방법이다. 둘 다 값을 필요할 때 하나씩 만들어(지연 평가, lazy evaluation) 메모리를 아낀다.

  • 콜백

    다른 함수에 인자로 넘겨서 그 함수가 대신 불러 주게 하는 함수. 동기로도, 비동기로도 불릴 수 있다.

  • Lombok

    Lombok은 게터·세터·생성자·toString 같은 반복 코드를 어노테이션으로 대신 생성해 주는 자바 라이브러리다. 컴파일할 때 어노테이션 처리기(annotation processor)가 바이트코드(bytecode)에 메서드를 넣는다. 스프링과는 별개 라이브러리라 따로 추가한다.

보기 옵션