본문 바로가기
프로그래밍 언어(Programming Languages)/파이썬(Python)

[Python] 파이썬 데코레이터: 코드의 아름다움을 재구성하는 방법

by 데이터 벌집 2023. 10. 31.
반응형

데코레이터는 파이썬 프로그래머들 사이에서 매력적인 도구로 알려져 있습니다. 간결하면서도 효율적인 코드를 작성하는 데 큰 도움을 주기 때문이죠. 이 글에서는 데코레이터의 기본 원리부터 실용적인 활용 방법까지 깊게 탐색해보려 합니다.

 

decorator - 코드의 아름다움을 재구성하는 방법


데코레이터란 무엇인가?

 

함수와 클래스에 대한 간단한 복습

 

파이썬에서 함수는 def 키워드로 정의되며, 입력값을 받아 처리 후 결괏값을 반환하는 역할을 합니다. 클래스는 class 키워드를 사용하여 정의하며, 객체 지향 프로그래밍의 핵심 요소입니다. 클래스 안에는 메서드와 속성이 포함될 수 있습니다.

 

def function_example(a, b):
    return a + b

class ClassExample:
    def __init__(self, data):
        self.data = data
    
    def method_example(self):
        return self.data * 2

데코레이터로 파이썬 함수 강화하기

 

데코레이터는 파이썬에서의 강력한 도구로, 기존 함수를 수정하지 않고도 그 함수의 출력을 수정할 수 있게 해 줍니다

  • 기존 함수를 수정하지 않고 새로운 기능을 추가할 수 있습니다.
  • 코드를 보다 간결하고 효율적으로 작성할 수 있습니다.
  • 코드의 재사용성을 높일 수 있습니다.
def decorator(func):
    def wrapper():
        print("Before function call")
        func()
        print("After function call")
    return wrapper

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

say_hello()  
# Output:
# Before function call
# Hello!
# After function call

활용 예제들

 

1. 인자를 로깅하는 데코레이터

 

많은 경우에, 함수가 어떤 인자와 함께 호출되었는지 로깅하고 싶을 수 있습니다.

 

아래의 데코레이터는 함수 호출 시 인자를 로깅합니다.

 

def log_args_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"Function {func.__name__} called with arguments {args} and keyword arguments {kwargs}.")
        return func(*args, **kwargs)
    return wrapper

@log_args_decorator
def add(a, b):
    return a + b

add(5, 3)  
# Output: Function add called with arguments (5, 3) and keyword arguments {}.

2. 함수 실행 시간 측정 데코레이터

 

함수의 실행 시간을 측정하여, 성능 최적화가 필요한 부분을 파악할 수 있습니다.

 

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:.2f} seconds to run.")
        return result
    return wrapper

@timing_decorator
def simulate_long_task():
    time.sleep(2)

simulate_long_task()  
# Output: Function simulate_long_task took 2.00 seconds to run.

 

3. 권한 확인 데코레이터

 

API 또는 웹 애플리케이션에서 특정 역할을 가진 사용자만 함수나 메서드를 실행할 수 있도록 권한을 검사하는 데코레이터입니다.

def admin_required(func):
    def wrapper(user, *args, **kwargs):
        if user.get('role') != 'admin':
            raise PermissionError("Only admin users can execute this function.")
        return func(user, *args, **kwargs)
    return wrapper

@admin_required
def view_admin_dashboard(user):
    return "Welcome to the admin dashboard!"

user = {'username': 'john', 'role': 'admin'}
print(view_admin_dashboard(user))  # Output: Welcome to the admin dashboard!

user = {'username': 'jane', 'role': 'member'}
# Raises PermissionError: Only admin users can execute this function.

 


사용할 때 주의점

 

데코레이터를 사용할 때 주의해야 할 몇 가지 중요한 사항이 있습니다. 이러한 주의점을 이해하고 올바른 방법으로 데코레이터를 구현하면 예기치 않은 버그나 이슈를 피할 수 있습니다.

 

1. 원래 함수의 메타데이터 유지

 

데코레이터를 사용하면 함수의 원래 메타데이터 (예: 이름, 문서 문자열)가 손실될 수 있습니다. 이를 방지하기 위해 functools.wraps를 사용하여 원래 함수의 메타데이터를 데코레이터 함수에 복사할 수 있습니다.

 

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 데코레이터의 기능 구현
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def my_function():
    """이것은 예제 함수입니다."""
    pass

print(my_function.__name__)  # 'my_function'
print(my_function.__doc__)   # '이것은 예제 함수입니다.'

 

functools.wraps를 사용하지 않으면, my_function.__name__은 'wrapper'로 출력되었을 것입니다.

 

2. 인자를 받는 데코레이터 구현

 

데코레이터가 인자를 받을 수 있도록 구현할 때는 추가적인 함수 레벨이 필요합니다. 이를 주의 깊게 처리하지 않으면 데코레이터의 동작에 문제가 발생할 수 있습니다.

 

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

@repeat(num_times=3)
def greet(name):
    print(f"안녕하세요, {name}!")

greet("홍길동")  # "안녕하세요, 홍길동!"이 세 번 출력됩니다.

 

3. 반환 값 다루기

 

데코레이터 내에서 원래 함수의 반환 값을 적절히 처리하지 않으면, 데코레이터를 적용한 함수의 반환 값이 예상과 다를 수 있습니다. 따라서 데코레이터를 구현할 때는 원래 함수의 반환 값을 적절히 처리하도록 주의해야 합니다.

 

def double_return(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result * 2
    return wrapper

@double_return
def add(a, b):
    return a + b

print(add(2, 3))  # 예상 결과: 5, 실제 결과: 10

add 함수의 반환 값이 두 배로 증가하는 것을 확인할 수 있습니다.

 

이러한 주의점을 기억하며 데코레이터를 구현하면, 데코레이터의 예상치 않은 부작용을 최소화할 수 있습니다.


데코레이터는 파이썬 프로그래머들 사이에서 매력적인 도구로 알려져 있습니다. 간결하면서도 효율적인 코드를 작성하는 데 큰 도움을 주기 때문이죠. 이 글에서는 데코레이터의 기본데코레이터는 파이썬에서 강력한 코드 최적화 도구로써, 올바르게 활용한다면 코드의 품질을 크게 향상할 수 있습니다. 그렇기에 데코레이터의 원리와 활용 방법을 꼭 숙지하시길 바랍니다. 이 글을 통해 데코레이터의 기본적인 이해와 활용 방법에 대한 아이디어를 얻으셨기를 바라며, 다음 글에서 또 다른 유익한 파이썬 팁을 공유하겠습니다. 감사합니다!

반응형