decorator

  • python의 데코레이터는 다른 함수를 수정하거나 확장하기 위한 도구이다.
  • 함수를 인자로 받아 해당 함수를 수정하거나 래핑하는 함수로, 함수의 기능을 확장/수정/ 함수 호출 전후에 추가 작업을 수행할 수 있도록 한다.

def myDecorator(func):
	def wrapper():
    	print('함수를 불러오기 전에 실행될 액션')
        func()
        print('함수를 불러오고 나서 실행될 액션')
    return wrapper

@myDecorator
def printHello():
	print('Hello!')
    
    
printHello()

# output
함수를 불러오기 전에 실행될 액션
Hello
함수를 불러오고 나서 실행될 액션

여기서 'myDecorator' 함수가 다른 함수를 인자로 받아 해당 함수를 수정하는 역할을 한다.
'wrapper' 함수 내부에서 전처리 작업과 후처리 작업을 수행하고 원래 함수를 호출한다.
'@myDecorator' 구문은 printHello() 함수 위에 데코레이터를 적용하는 구문으로, 'printHello' 함수를 'myDecorator' 함수로 래핑하는 것과 동일하다.
-> 'printHello' 함수가 호출될 때 전처리 작업, 함수 호출, 후처리 작업이 수행된다.

wrapper function

python에서 wrapper 함수는 일반적으로 다른 함수를 감싸거나 래핑하는 함수이다.
wrapper 함수는 일만적으로 데코레이터나 다른 고차 함수에서 사용된다.

주로 다음과 같은 목적을 위해 사용되는데

  • 원본 함수의 실행 전/후에 작업을 추가
    : wrapper 함수는 원본 함수를 추가하기 전에 어떤 작업을 수행하고, 원본 함수의 실행 후에 추가 작업을 수행할 수 있다.
    예를 들어 로깅, 인증, 캐싱 등의 작업이다.

  • 인자를 조작하거나 결과를 반환
    : wrapper 함수는 원본 함수에 전달되는 인자를 수정하거나 원본 함수의 결과를 가공하여 반환할 수 있다.
    이를 통해 입력값을 가공하거나 함수의 반환값을 변경할 수 있다.


def myDecorator(func):
	def wrapper(*args, **kwargs):
    	print('함수 호출 전')
        result = func(*args, **kwargs)
        print('함수 호출 후')
        return result
    return wrapper
    

%myDecorator
def add(a,b):
	return a+b
    
result = add(3,5)
print(result)

## output
함수 호출 전
함수 호출 후
8

위와 같이 wrapper 함수를 사용하면 원본 함수의 동작을 수정하거나 확장할 수 있다.

decorator 사용 이유

원본 함수를 수정하거나 확장 시 굳이 데코레이터를 사용해야할까?
사실 기능의 모듈화와 재사용성의 측면에서 데코레이터를 사용하는 것이 좋다.

  • 모듈화와 재사용성
    : 데코레이터를 사용해 기능을 분리해 모듈화가 가능하다. 한 번 정의된 데코레이터는 여러 함수에서 재사용할 수 있고, 코드의 가독성과 유지 보수성을 향상시킨다.
  • 원본 함수의 변경 없이 기능 추가
    : 원본 함수의 코드를 변경하지 않고도 새로운 기능인 로깅, 인증, 성능 측정과 같은 부가 기능을 추가할 수 있다.

데코레이터 사용 예시

[1] 인증 기능 제공 데코레이터

예를 들어 간단한 인증 기능을 제공하는 데코레이터를 구현하자면

def authenticate(func):
	def wrapper(*args, **kwargs):
    # 첫 번째 인자가 username으로 전달됐다고 가정
    	if args and args[0] == 'admin':
        	print('Authentication successful!')
            return func(*args, **kwargs)
        else:
        	print('Authentication failed!)
            return None
     	return wrapper
        
# 데코레이터를 사용해 인증이 필요한 서비스 함수 정의
@authenticate
def create_user(username, password):
	print(f'User {username} create with password {password}')

@authenticate
def delete_user(username):
	print(f'User {username} deleted')
    
# 서비스 함수 호출
create_user('admin', 'adminPassword')
create_user('guest', 'guestPassword')

delete_user('admin')
delete_user('guest')

위 코드에서 'authenticate' 데코레이터는 인증 기능을 제공하는데, 이 데코레이터는 함수를 래핑하고 인증이 성공하면 원본 함수를 호출하고 그렇지 않으면 함수를 호출하지 않고 None을 return 한다.

여기서는 'create_user'와 'delete_user'에 데코레이터를 적용해서, 인증이 필요한 서비스임을 알리고 인증 과정은 데코레이터 내부에서 처리된다.

이렇게 하면 각 서비스 함수 코드에 인증 로직이 없고, 인증 관련 로직은 하나의 장소에거 관리되기 때문에 코드의 모듈화와 재사용성이 향상된다.

[2] 로깅, 인증, 성능 측정 부가 기능을 하는 데코레이터 추가

import time

# 로깅 데코레이터
def log(func):
	def wrapper(*args, **kwargs):
    	print(f'{func.__name__} 함수 호출, 해당 args: {args}, 해당 kwargs : {kwargs}')
        return func(*args, **kwargs)
    return wrapper
    
    
# 인증 데코레이터
def authenticate(func):
	def wrapper(*args, **kwargs):
    	if kwargs.get('authenticated'):
        	return func(*args, **kwargs)
        else:
        	print('Authentication failed!')
            return None
    return wrapper
    
# 성능 측정 데코레이터
def measure_performance(func):
	def wrapper(*args, **kwargs):
    	start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f" {func.__name__} 수행 시간 : {end_time-start_time} s")
   		return result
   return wrapper
   
# 데코레이터를 사용해 서비스 함수 정의
@log
@authenticate
@measure_performance
def process_data(data, authenticated=False):
	time.sleep(2) # 시간이 소요되는 작업이라고 가정
    print(f"Data processed : {data}")
    
# 서비스 함수 호출
process_data('Sample data', authenticated=True)

위 코드는 세 가지 데코레이터인
log, authenticate, measure_performance 를 정의하고, 'process_data' 서비스 함수를 구현했다.

  • log : 함수 호출 시 인자와 함께 호출 됐음을 기록
  • authenticate : 함수가 호출되기 전에 인증을 확인하고 인증이 되어 있지 않으면 실행되지 않음
  • measure_performance : 함수의 실행 시간을 측정하여 출력

위와 같이 각각 서비스 함수에 부가 기능에 대한 코드가 들어가지 않지만, 데코레이터를 통해 이러한 부가 기능을 함수에 적용하게 되고 반복하지만 코드의 모듈화와 재사용성, 가독성을 향상시킨다.

profile
꿈꾸는 것도 개발처럼 깊게

0개의 댓글