[파이썬] 데코레이터 (2)

대심이·2024년 1월 28일

파이썬 클린 코드

목록 보기
3/5
post-thumbnail

이전 글 [파이썬] 데코레이터 (1)에 이어서 작성

데코레이터의 특징

여러 기능과 같이 사용 가능

함수, 클래스뿐만이 아니라 제네레이터, 코루틴, 데코레이트된 객체에도 사용 가능하다.

Stack으로 쌓기

아래처럼 여러 개를 함께 사용할 수 있다.

@DataClass
@Serialization(
    username=show_original,
    password=hide_field,
    ip=show_original,
    timestamp=format_time,
)
class LoginEvent:

중첩 함수, 고차 함수

데코레이터의 구조는 함수 안의 함수로 중첩함수 형태다. 여기에 함수를 인자로 받고 함수를 반환하는 고차 함수의 구조도 가져간다.

아래 예제를 보면 wrapped 내에서 정의한 함수를 반환하여, 외부 함수trace가 종료되더라도 내부 함수는 유지되는 클로저 형태이다.

def trace(fn):
	@wraps(fn)
    def wrapped(*args, **kwargs):
        print("before", fn.__name__)
        result = fn(*args, **kwargs)
        print("after", fn.__name__)
        return result
    return wrapped

주의사항

데코레이터는 래핑하여 반환하기에 함수명과 docstring이 변경된다. 이후에 디버깅 또는 오류 추적시 문제가 될 수 있다.

from functools import wraps

def trace(fn):
    def wrapped(*args, **kwargs):
				"""trace 함수"""
        print("before", fn.__name__)
        result = fn(*args, **kwargs)
        print("after", fn.__name__)
        return result
    return wrapped

@trace
def hello(name):
		"""hello 출력 함수"""
    print("Hello,", name)

print(hello) #=> <function trace.<locals>.wrapped at 0xa9a7c0>
help(hello) #=> trace 함수

이처럼 데코레이터 함수의 정보가 출력된다. 이를 방지하기 위해 @wraps 사용을 권장한다.

from functools import wraps

def trace(fn):
    @wraps(fn)
    def wrapped(*args, **kwargs):
        # ...
    return wrapped

@trace
def hello(name):
		"""hello 출력 함수"""
    print("Hello,", name)

print(hello) #=> <function hello at 0x13d9b60>
help(hello) #=> hello 출력 함수

이렇게 함으로 원본 함수를 유지할 수 있다. 물론 __wrapped__로 원본 함수에도 접근할 수 있다. 보통 테스트시 사용한다.

print(hello.__wrapped__("User")) #=> trace가 붙지 않은 원본 함수 출력

사용 규칙

마지막으로 책에서 언급하는 데코레이터 사용시 규칙으로 글을 마무리한다.

DRY를 따르자

처음부터 데코레이터를 만드는 것이 아니라 일정 패턴이 생겨 추상화가 필요할 때, Do not repeat yourself를 실천하면 된다.

최소 3번 이상의 패턴이 있을 때 쓰며, 데코레이터 코드 자체는 최소한으로 유지해야 한다.

profile
대범해지고 싶어서 대심이

0개의 댓글