[Python] Decorator

HyoSeok·2024년 10월 2일

데코레이터(Decorator)는 Python에서 함수나 메서드의 동작을 수정하거나 확장할 때 유용하게 사용됩니다. 데코레이터는 주어진 함수를 감싸서 새로운 동작을 추가하는 함수입니다. 즉, 기존 코드의 수정 없이 새로운 기능을 추가할 수 있는 도구입니다.

데코레이터는 다양한 상황에서 사용할 수 있는데, 주로 다음과 같은 경우에 사용됩니다:

1. 코드 재사용

여러 함수에 동일한 기능(로깅, 인증, 시간 측정 등)을 적용해야 할 때, 데코레이터를 사용하면 중복된 코드를 피할 수 있습니다. 한 번 데코레이터를 정의하면, 여러 함수에 쉽게 적용할 수 있습니다.

예: 여러 함수에 로깅을 추가해야 하는 경우

def logging_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"Function {func.__name__} is called")
        result = func(*args, **kwargs)
        print(f"Function {func.__name__} has finished")
        return result
    return wrapper

@logging_decorator
def my_function(x, y):
    return x + y

my_function(3, 4)

이 경우 logging_decoratormy_function을 감싸서 호출 전후에 로그 메시지를 출력합니다.

2. 반복되는 작업을 추상화

함수나 메서드에서 반복적으로 수행해야 하는 작업(예: 입력 유효성 검사, 권한 확인 등)을 추상화하여 데코레이터로 만들 수 있습니다.

예: 함수가 실행되기 전에 권한을 확인하는 경우

def check_permissions(func):
    def wrapper(*args, **kwargs):
        if not user_has_permission():
            raise PermissionError("You do not have permission to execute this function.")
        return func(*args, **kwargs)
    return wrapper

@check_permissions
def delete_user(user_id):
    print(f"Deleting user {user_id}")

여기서 check_permissions 데코레이터는 권한을 확인한 후에만 delete_user 함수가 실행되도록 합니다.

3. 로깅 및 디버깅

함수의 실행 과정을 추적하고, 실행 시간을 기록하거나 디버깅 정보를 출력하는 데 데코레이터를 사용할 수 있습니다.

예: 함수의 실행 시간을 측정하는 데코레이터

import time

def timing_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Function {func.__name__} took {end_time - start_time} seconds to execute")
        return result
    return wrapper

@timing_decorator
def long_running_function():
    time.sleep(2)  # 2초 동안 일시 중지
    return "Finished"

long_running_function()

이 데코레이터는 함수의 실행 시간을 측정하고 출력해 줍니다.

4. 입력값을 수정하거나 검증

함수에 전달된 인자를 사전 처리하거나 유효성을 검사하는 데 사용할 수 있습니다.

예: 함수의 인자에 대한 검증

def validate_arguments(func):
    def wrapper(x, y):
        if x < 0 or y < 0:
            raise ValueError("Both arguments must be non-negative")
        return func(x, y)
    return wrapper

@validate_arguments
def add_positive_numbers(x, y):
    return x + y

print(add_positive_numbers(5, 3))  # 정상 실행
print(add_positive_numbers(-1, 3))  # 예외 발생

여기서 validate_arguments 데코레이터는 함수에 전달된 인자가 유효한지 확인하고, 유효하지 않으면 예외를 발생시킵니다.

5. 캐싱

결과를 캐싱해서 동일한 입력에 대해 반복된 호출 시 계산을 생략하고 이전에 계산된 값을 반환하도록 하는 데 사용됩니다.

예: 캐싱 기능 구현

def cache_decorator(func):
    cache = {}
    def wrapper(*args):
        if args in cache:
            print("Returning cached result")
            return cache[args]
        result = func(*args)
        cache[args] = result
        return result
    return wrapper

@cache_decorator
def slow_function(x):
    time.sleep(2)
    return x * 2

print(slow_function(2))  # 처음 실행, 2초 소요
print(slow_function(2))  # 캐시된 결과 반환

이 데코레이터는 동일한 인자로 호출된 함수의 결과를 캐싱하여, 두 번째 호출 시 시간을 절약합니다.

6. 메소드 데코레이터 (클래스에서 사용)

데코레이터는 함수뿐만 아니라 클래스 메소드에도 적용될 수 있습니다. 이를 통해 클래스의 메소드의 동작을 확장하거나 수정할 수 있습니다.

예: 메소드 실행 전후에 로깅하는 데코레이터

def method_logger(func):
    def wrapper(self, *args, **kwargs):
        print(f"Calling method {func.__name__}")
        result = func(self, *args, **kwargs)
        print(f"Method {func.__name__} completed")
        return result
    return wrapper

class MyClass:
    @method_logger
    def my_method(self):
        print("Method is running")

obj = MyClass()
obj.my_method()

7. 클래스 데코레이터

함수뿐만 아니라 클래스에도 데코레이터를 적용할 수 있습니다. 클래스 데코레이터는 주로 클래스 인스턴스의 동작을 수정하거나 특정 속성을 추가하는 데 사용됩니다.

def add_class_info(cls):
    cls.class_info = "This is a decorated class"
    return cls

@add_class_info
class MyClass:
    pass

print(MyClass.class_info)  # "This is a decorated class"

결론

데코레이터는 반복되는 작업의 추상화, 코드 재사용성 향상, 함수의 동작 수정 및 확장에 매우 유용한 도구입니다. 주로 로깅, 권한 검사, 시간 측정, 입력 검증, 캐싱 등의 작업에 사용되며, 코드의 가독성을 높이고 유지보수성을 향상시킬 수 있습니다.

profile
hola!

0개의 댓글