본문 바로가기

Study Information Technology

파이썬 데코레이터의 이해와 활용

728x90
반응형

파이썬 데코레이터의 이해와 활용

Overview

파이썬의 데코레이터는 함수나 메서드의 동작을 동적으로 확장하거나 수정할 수 있는 강력한 도구입니다. 이 기능은 코드의 재사용성과 가독성을 높이면서도, 기존 코드에 손쉽게 새로운 기능을 추가할 수 있게 도와줍니다. 데코레이터는 함수나 메서드를 감싸서 그 기능을 확장하는 함수입니다. 이 글에서는 파이썬의 데코레이터에 대해 깊이 있는 설명과 함께 구체적인 예시를 제공하고, 발생할 수 있는 에러와 그 해결 방법도 다룰 것입니다.

데코레이터란 무엇인가?

데코레이터는 다른 함수를 인자로 받아서 그 함수의 동작을 변경하거나 확장하는 함수를 의미합니다. 데코레이터를 사용하면 기존 코드를 수정하지 않고도 새로운 기능을 추가할 수 있습니다. 이때 데코레이터는 기존 함수를 감싸는 역할을 하며, 함수의 전후에 추가적인 동작을 정의할 수 있습니다.

기본적인 데코레이터 구조

데코레이터는 보통 다음과 같은 구조를 가집니다:

def decorator_function(original_function):
def wrapper_function():
# 함수 실행 전 작업
print("Wrapper executed this before {}".format(original_function.__name__))

# 원본 함수 실행
original_function()

# 함수 실행 후 작업
print("Wrapper executed this after {}".format(original_function.__name__))

return wrapper_function

이 구조를 사용하여, 데코레이터를 적용할 함수에 대해 @decorator_function과 같은 문법으로 데코레이터를 지정할 수 있습니다. 아래는 이를 활용한 예제입니다:

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

이 경우, say_hello 함수가 호출되면 wrapper_function이 실행되어 함수 실행 전후에 지정된 메시지를 출력합니다.

데코레이터의 활용 예시

1. 로깅 데코레이터

데코레이터를 활용하여 함수의 실행을 로그로 남길 수 있습니다. 이 예제에서는 함수 호출 시마다 로그를 기록하는 데코레이터를 구현합니다.

import time

def log_execution_time(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
execution_time = end_time - start_time
print(f"Function '{func.__name__}' executed in {execution_time:.4f} seconds")
return result
return wrapper

@log_execution_time
def compute_sum(a, b):
time.sleep(1)  # Simulate a time-consuming operation
return a + b

print(compute_sum(5, 7))

이 코드는 compute_sum 함수가 실행되는 시간을 측정하고 로그로 출력합니다. @log_execution_time 데코레이터가 compute_sum 함수의 실행 전후에 시간을 측정하고 결과를 출력하는 역할을 합니다.

2. 접근 제어 데코레이터

접근 제어 데코레이터를 사용하여 함수 호출 시 특정 조건을 검사할 수 있습니다. 예를 들어, 사용자가 관리자 권한을 가지고 있는지 확인하는 데코레이터를 만들 수 있습니다.

def require_admin(func):
def wrapper(user, *args, **kwargs):
if not user.get('is_admin', False):
raise PermissionError("User does not have admin rights.")
return func(user, *args, **kwargs)
return wrapper

@require_admin
def delete_user(user, user_id):
print(f"User {user_id} deleted by admin {user['name']}")

# 사용 예
user = {'name': 'Alice', 'is_admin': True}
delete_user(user, 123)

user = {'name': 'Bob', 'is_admin': False}
try:
delete_user(user, 123)
except PermissionError as e:
print(e)

이 데코레이터는 사용자가 관리자 권한을 가진 경우에만 delete_user 함수를 실행하도록 합니다. 권한이 없는 경우 PermissionError를 발생시킵니다.

데코레이터의 인자와 동적 인자 처리

데코레이터는 인자를 받을 수 있습니다. 이를 통해 보다 유연한 데코레이터를 구현할 수 있습니다. 다음은 인자를 사용하는 데코레이터의 예입니다:

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")

위 코드는 greet 함수를 3번 반복해서 호출합니다. repeat 데코레이터는 호출 횟수를 인자로 받아서 그에 따라 함수를 반복 호출합니다.

에러 처리와 해결 방법

데코레이터를 사용할 때 종종 발생할 수 있는 에러와 그 해결 방법을 소개합니다.

1. TypeError: 'function' object is not callable

이 에러는 데코레이터를 정의할 때 return 문에서 함수가 아닌 다른 객체를 반환할 때 발생할 수 있습니다. 예를 들어, wrapper 대신 다른 값을 반환하는 경우입니다.

해결 방법:
데코레이터의 return 문이 함수 객체를 반환하도록 해야 합니다. 다음은 잘못된 예와 수정된 예입니다.

잘못된 예:

def faulty_decorator(func):
def wrapper():
pass
return "Not a function"

수정된 예:

def correct_decorator(func):
def wrapper():
pass
return wrapper

2. AttributeError: 'function' object has no attribute '__name__'

이 에러는 데코레이터 내부에서 func.__name__을 사용할 때 발생할 수 있습니다. 데코레이터가 잘못된 객체를 처리하거나 함수가 아닌 다른 객체를 전달받을 때 발생할 수 있습니다.

해결 방법:
데코레이터가 실제 함수 객체를 받도록 하고, func가 함수인지 확인하는 조건을 추가할 수 있습니다.

def safe_decorator(func):
if not callable(func):
raise TypeError("The decorated object must be callable")

def wrapper(*args, **kwargs):
return func(*args, **kwargs)

return wrapper

참고문서

  1. Python Decorators - Real Python
  2. Official Python Documentation - Functions as Objects
  3. Understanding Decorators in Python - GeeksforGeeks

이 글을 통해 파이썬 데코레이터의 개념과 활용 방법을 깊이 이해할 수 있기를 바랍니다. 데코레이터는 코드의 가독성을 높이고 재사용성을 강화하는 데 매우 유용한 도구입니다.

728x90
반응형