데코레이터(Decorator)는 Python에서 함수나 메서드의 동작을 수정하거나 확장할 때 유용하게 사용됩니다. 데코레이터는 주어진 함수를 감싸서 새로운 동작을 추가하는 함수입니다. 즉, 기존 코드의 수정 없이 새로운 기능을 추가할 수 있는 도구입니다.
데코레이터는 다양한 상황에서 사용할 수 있는데, 주로 다음과 같은 경우에 사용됩니다:
여러 함수에 동일한 기능(로깅, 인증, 시간 측정 등)을 적용해야 할 때, 데코레이터를 사용하면 중복된 코드를 피할 수 있습니다. 한 번 데코레이터를 정의하면, 여러 함수에 쉽게 적용할 수 있습니다.
예: 여러 함수에 로깅을 추가해야 하는 경우
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_decorator는 my_function을 감싸서 호출 전후에 로그 메시지를 출력합니다.
함수나 메서드에서 반복적으로 수행해야 하는 작업(예: 입력 유효성 검사, 권한 확인 등)을 추상화하여 데코레이터로 만들 수 있습니다.
예: 함수가 실행되기 전에 권한을 확인하는 경우
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 함수가 실행되도록 합니다.
함수의 실행 과정을 추적하고, 실행 시간을 기록하거나 디버깅 정보를 출력하는 데 데코레이터를 사용할 수 있습니다.
예: 함수의 실행 시간을 측정하는 데코레이터
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()
이 데코레이터는 함수의 실행 시간을 측정하고 출력해 줍니다.
함수에 전달된 인자를 사전 처리하거나 유효성을 검사하는 데 사용할 수 있습니다.
예: 함수의 인자에 대한 검증
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 데코레이터는 함수에 전달된 인자가 유효한지 확인하고, 유효하지 않으면 예외를 발생시킵니다.
결과를 캐싱해서 동일한 입력에 대해 반복된 호출 시 계산을 생략하고 이전에 계산된 값을 반환하도록 하는 데 사용됩니다.
예: 캐싱 기능 구현
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)) # 캐시된 결과 반환
이 데코레이터는 동일한 인자로 호출된 함수의 결과를 캐싱하여, 두 번째 호출 시 시간을 절약합니다.
데코레이터는 함수뿐만 아니라 클래스 메소드에도 적용될 수 있습니다. 이를 통해 클래스의 메소드의 동작을 확장하거나 수정할 수 있습니다.
예: 메소드 실행 전후에 로깅하는 데코레이터
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()
함수뿐만 아니라 클래스에도 데코레이터를 적용할 수 있습니다. 클래스 데코레이터는 주로 클래스 인스턴스의 동작을 수정하거나 특정 속성을 추가하는 데 사용됩니다.
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"
데코레이터는 반복되는 작업의 추상화, 코드 재사용성 향상, 함수의 동작 수정 및 확장에 매우 유용한 도구입니다. 주로 로깅, 권한 검사, 시간 측정, 입력 검증, 캐싱 등의 작업에 사용되며, 코드의 가독성을 높이고 유지보수성을 향상시킬 수 있습니다.