고급 프로그래머를 위한 Python 데코레이터 가이드

Python 데코레이터는 함수나 메서드의 동작을 수정하기 위한 강력하고 유연한 도구입니다. 프로그래머는 이를 통해 호출 가능한 객체의 기능을 깔끔하고 읽기 쉽고 유지 관리하기 쉬운 방식으로 확장하거나 변경할 수 있습니다. 이 문서에서는 중첩된 데코레이터, 데코레이터 인수, 클래스 기반 데코레이터를 포함하여 Python 데코레이터와 관련된 고급 개념을 살펴봅니다.

데코레이터란 무엇인가요?

데코레이터는 다른 함수의 동작을 수정하는 함수입니다. 다른 함수를 래핑하여 코드를 명시적으로 수정하지 않고 동작을 확장합니다. 데코레이터는 @decorator_name 구문을 사용하여 정의되며 함수 정의 위에 배치됩니다.

기본 데코레이터 구문

간단한 데코레이터는 함수를 인수로 받고, 일부 동작을 추가하는 내부 함수를 정의한 다음, 내부 함수를 반환합니다.

def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()

인수가 있는 데코레이터 함수

데코레이터는 인수를 허용함으로써 더 유연해질 수 있습니다. 이러한 데코레이터를 만들려면 데코레이터를 반환하는 함수를 작성해야 합니다. 이렇게 하면 데코레이터에 더 동적인 동작을 추가할 수 있습니다.

def repeat(num_times):
    def decorator_repeat(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                func(*args, **kwargs)
        return wrapper
    return decorator_repeat

@repeat(num_times=3)
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")

네스팅 데코레이터

데코레이터는 여러 동작을 결합하기 위해 중첩될 수 있습니다. 예를 들어, 단일 함수에 두 개 이상의 데코레이터를 사용할 수 있습니다.

def uppercase_decorator(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result.upper()
    return wrapper

def repeat_decorator(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result + result
    return wrapper

@repeat_decorator
@uppercase_decorator
def say_word(word):
    return word

print(say_word("hello"))

클래스 기반 데코레이터

파이썬에서 데코레이터는 __call__ 메서드를 사용하여 클래스로 구현될 수도 있습니다. 클래스 기반 데코레이터는 더 복잡한 상태 관리와 동작이 필요할 때 유용합니다.

class CountCalls:
    def __init__(self, func):
        self.func = func
        self.num_calls = 0

    def __call__(self, *args, **kwargs):
        self.num_calls += 1
        print(f"Call {self.num_calls} of {self.func.__name__!r}")
        return self.func(*args, **kwargs)

@CountCalls
def say_hello():
    print("Hello!")

say_hello()
say_hello()

functools.wraps를 사용하여 메타데이터 보존

데코레이터를 작성하면 데코레이팅된 함수는 이름과 docstring과 같은 원래 메타데이터를 잃습니다. functools.wraps 데코레이터를 사용하면 원래 함수의 메타데이터를 래퍼 함수로 복사할 수 있습니다.

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("Wrapper function executed before", func.__name__)
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def display_info(name, age):
    """Displays name and age."""
    print(f"display_info ran with arguments ({name}, {age})")

print(display_info.__name__)  # Output: display_info
print(display_info.__doc__)   # Output: Displays name and age.

결론

Python 데코레이터는 유연한 코드 디자인과 동작 수정을 가능하게 하는 강력한 기능입니다. 중첩된 데코레이터, 인수가 있는 데코레이터, 클래스 기반 데코레이터와 같은 고급 사용은 Python 프로그램에 더 많은 기능과 가독성을 제공할 수 있습니다. 데코레이터를 올바르게 이해하고 활용함으로써 개발자는 더 간결하고 효율적이며 읽기 쉬운 코드를 작성할 수 있습니다.