프로그래밍/Python

데코레이터 패턴 그리고 python 데코레이터

코드자국 2024. 7. 10. 20:52
반응형

데코레이터 패턴


데코레이터 패턴

데코레이터 패턴(Decorator pattern)이란 주어진 상황 및 용도에 따라 어떤 객체에 책임을 덧붙이는 패턴으로, 기능 확장이 필요할 때 서브클래싱 대신 쓸 수 있는 유연한 대안이 될 수 있다.

-위키백과

 

전에 Nestjs 디자인 패턴에도 언급을 했지만 대부분의 프레임워크에서 사용하는 데코레이트 패턴이 python에도 구현이 되어 있다.

 

유연한 확장성

 

 @staticmethod , @classmethod 와 같이 이미 python 기본 기능에 정의되어 사용 가능한 데코레이터도 있지만 사용자가 원한다면 만들어서 사용하는 커스텀 데코레이터도 사용 할 수가 있다.

 


custom decorator

 

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__}' executed in {end_time - start_time:.4f} seconds")
        return result
    return wrapper

# 데코레이터 사용
@timing_decorator
def example_function(n):
    total = 0
    for i in range(n):
        total += i
    return total

# 함수 호출
result = example_function(1000000)
print(f"Result: {result}")

 

위와 같이 timing_decorator를 example_function에 붙여서 사용 한 것 처럼. 유연하게 확장 가능한 기능을 만들 수 있다.

 

timing_decorator의 경우 함수의 종료시간을 측정 할 수가 있기 때문에 함수 또는 모듈의 성능 평가를 하는데 사용 할 수 있다.

 

 


접근 제어 데코레이터

 

def requires_permission(permission):
    def decorator(func):
        def wrapper(user, *args, **kwargs):
            if user.get('permission') == permission:
                return func(user, *args, **kwargs)
            else:
                raise PermissionError(f"User '{user.get('name')}' does not have {permission} permission")
        return wrapper
    return decorator

@requires_permission('admin')
def delete_user(user, user_id):
    print(f"User {user_id} deleted by {user.get('name')}")

# 사용자 정보
admin_user = {'name': 'Admin', 'permission': 'admin'}
regular_user = {'name': 'User', 'permission': 'user'}

# 함수 호출
delete_user(admin_user, 123)
# delete_user(regular_user, 123)  # PermissionError 발생

 

해당 코드는 권한에 따라 delete_user의 함수를 사용할 수 있는지를 결정하고 있다.


캐싱 데코레이터

 

def cache_decorator(func):
    cache = {}
    
    def wrapper(*args):
        if args in cache:
            print(f"Fetching from cache for arguments {args}")
            return cache[args]
        else:
            result = func(*args)
            cache[args] = result
            print(f"Caching result for arguments {args}")
            return result
    
    return wrapper

@cache_decorator
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

# 함수 호출
print(fibonacci(10))
print(fibonacci(10))  # 캐시된 결과 반환

 

해당 코드는 cache 리스트에 args에 따른 결과를 저장하여 연산량이 많은 함수에 대해 반복 연산을 하지 않도록 하는 데코레이터 이다.

 

 

 


마치며

 

python의 데코레이터는 쉽게 구현 할 수 있는 반면에 사용하여 얻을 수 있는 효과는 강력하다. 프로그래밍을 할 때 decorator 함수를 만들어 사용하는 습관을 들인다면 가독성도 높고 확장 및 수정도 용이한 좋은 코드를 짤 수 있을 것이다.

 

 

 

 

반응형