프로그래밍 언어/Python

파이썬 데코레이터 가이드: 함수 래핑, 로깅, 캐싱 및 타임아웃 활용법 - Python 기초 #13

eco7T 2024. 5. 24. 08:55
반응형

이 글은 파이썬 데코레이터에 대한 이해를 돕기 위해 작성되었습니다. 데코레이터의 기본 개념, 사용법, 그리고 실용적인 예시들을 통해 함수의 행동을 수정하고 확장하는 방법을 설명합니다. 이를 통해 코드의 가독성을 높이고 유지보수를 쉽게 할 수 있도록 하는 것이 목적입니다.

파이썬 데코레이터
파이썬 데코레이터

 

파이썬 데코레이터

데코레이터는 함수의 역할을 수정하는 특이한 방법인데요, 이러한 방법을 통해 코드를 간결하고 유지보수가 쉽게 만들 수 있습니다. 데코레이터는 다른 함수를 인수로 받아 "래핑(wrap)"하여 그 함수의 기능을 확장하거나 수정할 수 있습니다.

  • 예시 코드:
def decorator_func(original_func): def wrapper_func(*args, **kwargs): print(f"래핑 전: {args}, {kwargs}") result = original_func(*args, **kwargs) print(f"래핑 후: {result}") return result return wrapper_func @decorator_func def display(): print("display 함수가 실행되었습니다.") display()
  • 출력:
래핑 전: (), {} display 함수가 실행되었습니다. 래핑 후: None

이 예시 코드에서 `display` 함수는 `decorator_func`로 데코레이팅되었습니다. 실행할 때마다 `display` 함수 전후에 추가 코드가 실행되는 것을 볼 수 있습니다.

 

파이썬 데코레이터 사용 방법

  • 데코레이터 함수: 다른 함수를 인수로 받아 래핑 하는 함수입니다.
  • 래퍼(Wrapper) 함수: 원래 함수를 감싸고 추가 기능을 제공하는 함수입니다.
  • 원래 함수: 데코레이터가 래핑하는 함수입니다.
  • 데코레이터는 `@` 문법을 통해 적용됩니다. 이렇게 하면 함수의 이름이 래퍼 함수로 바인딩됩니다.

사례 1. 로깅 데코레이터

from datetime import datetime def log_decorator(original_func): def wrapper_func(*args, **kwargs): result = original_func(*args, **kwargs) print(f"{datetime.now()}: {original_func.__name__} 함수가 실행되었습니다. 결과: {result}") return result return wrapper_func @log_decorator def add_numbers(a, b): return a + b result = add_numbers(3, 5) print(result)
  • 이 데코레이터는 함수가 실행될 때마다 시간과 함수 이름, 결과를 로깅합니다. 디버깅이나 실행 추적에 유용합니다.

사례 2. 캐싱 데코레이터

cache = {} def cache_decorator(original_func): def wrapper_func(*args, **kwargs): key = str(args) + str(kwargs) if key in cache: print(f"캐시에서 값을 가져왔습니다: {cache[key]}") return cache[key] else: result = original_func(*args, **kwargs) cache[key] = result print(f"캐시에 값을 저장했습니다: {result}") return result return wrapper_func @cache_decorator def expensive_func(a, b): print("계산 중...") return a ** b result1 = expensive_func(2, 3) result2 = expensive_func(2, 3) # 캐싱된 값을 사용합니다.
  • 이 데코레이터는 함수의 입력과 출력을 캐싱합니다. 같은 입력이 주어지면 캐시 된 결과를 반환하므로 불필요한 계산을 피할 수 있습니다.

사례 3. 타임아웃 데코레이터

import signal from functools import wraps class TimeoutError(Exception): pass def timeout_decorator(timeout_secs): def decorator(original_func): @wraps(original_func) def wrapper_func(*args, **kwargs): def handler(signum, frame): raise TimeoutError(f"{original_func.__name__} 함수가 {timeout_secs}초 동안 실행되지 않아 중단되었습니다.") signal.signal(signal.SIGALRM, handler) signal.alarm(timeout_secs) try: result = original_func(*args, **kwargs) finally: signal.alarm(0) return result return wrapper_func return decorator @timeout_decorator(5) def slow_func(n): import time time.sleep(n) return n try: result = slow_func(3) print(f"결과: {result}") except TimeoutError as e: print(e) try: result = slow_func(10) print(f"결과: {result}") except TimeoutError as e: print(e)
  • 이 데코레이터는 함수 실행 시간을 제한합니다. 지정된 시간이 지나면 `TimeoutError`를 발생시켜 함수 실행을 중단합니다.

 

이러한 실용적인 예시들을 통해 데코레이터가 코드를 더 유연하고 재사용 가능하게 만들어주는 것을 알 수 있습니다.

사용 시 주의해야 할 사항으로는:

  • 데코레이터는 중첩될 수 있습니다. 여러 개의 데코레이터를 함수에 적용할 수 있습니다.
  • 래퍼 함수 내에서 원래 함수의 메타데이터(docstring, name, annotations 등)에 접근하려면 `functools.wraps` 데코레이터를 사용하는 것이 좋습니다.

데코레이터는 파이썬의 강력한 기능 중 하나입니다. 이 가이드북을 통해 데코레이터의 기본 개념과 다양한 활용 방법을 익혔으니 직접 연습해 보며 이해도를 높이는 것이 좋습니다.

반응형